Python-checkins
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
August 2021
- 1 participants
- 478 discussions
bpo-43913: Fix bugs in cleaning up classes and modules in unittest. (GH-28006)
by ambv Aug. 30, 2021
by ambv Aug. 30, 2021
Aug. 30, 2021
https://github.com/python/cpython/commit/08d9e597c8ef5a2b26375ac954fdf224f5…
commit: 08d9e597c8ef5a2b26375ac954fdf224f5d82c3c
branch: main
author: Serhiy Storchaka <storchaka(a)gmail.com>
committer: ambv <lukasz(a)langa.pl>
date: 2021-08-30T18:25:59+02:00
summary:
bpo-43913: Fix bugs in cleaning up classes and modules in unittest. (GH-28006)
* Functions registered with addModuleCleanup() were not called unless
the user defines tearDownModule() in their test module.
* Functions registered with addClassCleanup() were not called if
tearDownClass is set to None.
* Buffering in TestResult did not work with functions registered
with addClassCleanup() and addModuleCleanup().
* Errors in functions registered with addClassCleanup() and
addModuleCleanup() were not handled correctly in buffered and
debug modes.
* Errors in setUpModule() and functions registered with
addModuleCleanup() were reported in wrong order.
* And several lesser bugs.
files:
A Misc/NEWS.d/next/Library/2021-08-27-23-40-51.bpo-43913.Uo1Gt5.rst
M Lib/unittest/suite.py
M Lib/unittest/test/test_result.py
M Lib/unittest/test/test_runner.py
diff --git a/Lib/unittest/suite.py b/Lib/unittest/suite.py
index 41993f9cf69af..6f45b6fe5f603 100644
--- a/Lib/unittest/suite.py
+++ b/Lib/unittest/suite.py
@@ -149,6 +149,7 @@ def _handleClassSetUp(self, test, result):
if getattr(currentClass, "__unittest_skip__", False):
return
+ failed = False
try:
currentClass._classSetupFailed = False
except TypeError:
@@ -157,27 +158,32 @@ def _handleClassSetUp(self, test, result):
pass
setUpClass = getattr(currentClass, 'setUpClass', None)
+ doClassCleanups = getattr(currentClass, 'doClassCleanups', None)
if setUpClass is not None:
_call_if_exists(result, '_setupStdout')
try:
- setUpClass()
- except Exception as e:
- if isinstance(result, _DebugResult):
- raise
- currentClass._classSetupFailed = True
- className = util.strclass(currentClass)
- self._createClassOrModuleLevelException(result, e,
- 'setUpClass',
- className)
+ try:
+ setUpClass()
+ except Exception as e:
+ if isinstance(result, _DebugResult):
+ raise
+ failed = True
+ try:
+ currentClass._classSetupFailed = True
+ except TypeError:
+ pass
+ className = util.strclass(currentClass)
+ self._createClassOrModuleLevelException(result, e,
+ 'setUpClass',
+ className)
+ if failed and doClassCleanups is not None:
+ doClassCleanups()
+ for exc_info in currentClass.tearDown_exceptions:
+ self._createClassOrModuleLevelException(
+ result, exc_info[1], 'setUpClass', className,
+ info=exc_info)
finally:
_call_if_exists(result, '_restoreStdout')
- if currentClass._classSetupFailed is True:
- currentClass.doClassCleanups()
- if len(currentClass.tearDown_exceptions) > 0:
- for exc in currentClass.tearDown_exceptions:
- self._createClassOrModuleLevelException(
- result, exc[1], 'setUpClass', className,
- info=exc)
def _get_previous_module(self, result):
previousModule = None
@@ -205,20 +211,22 @@ def _handleModuleFixture(self, test, result):
if setUpModule is not None:
_call_if_exists(result, '_setupStdout')
try:
- setUpModule()
- except Exception as e:
try:
- case.doModuleCleanups()
- except Exception as exc:
- self._createClassOrModuleLevelException(result, exc,
+ setUpModule()
+ except Exception as e:
+ if isinstance(result, _DebugResult):
+ raise
+ result._moduleSetUpFailed = True
+ self._createClassOrModuleLevelException(result, e,
'setUpModule',
currentModule)
- if isinstance(result, _DebugResult):
- raise
- result._moduleSetUpFailed = True
- self._createClassOrModuleLevelException(result, e,
- 'setUpModule',
- currentModule)
+ if result._moduleSetUpFailed:
+ try:
+ case.doModuleCleanups()
+ except Exception as e:
+ self._createClassOrModuleLevelException(result, e,
+ 'setUpModule',
+ currentModule)
finally:
_call_if_exists(result, '_restoreStdout')
@@ -251,30 +259,33 @@ def _handleModuleTearDown(self, result):
except KeyError:
return
- tearDownModule = getattr(module, 'tearDownModule', None)
- if tearDownModule is not None:
- _call_if_exists(result, '_setupStdout')
+ _call_if_exists(result, '_setupStdout')
+ try:
+ tearDownModule = getattr(module, 'tearDownModule', None)
+ if tearDownModule is not None:
+ try:
+ tearDownModule()
+ except Exception as e:
+ if isinstance(result, _DebugResult):
+ raise
+ self._createClassOrModuleLevelException(result, e,
+ 'tearDownModule',
+ previousModule)
try:
- tearDownModule()
+ case.doModuleCleanups()
except Exception as e:
if isinstance(result, _DebugResult):
raise
self._createClassOrModuleLevelException(result, e,
'tearDownModule',
previousModule)
- finally:
- _call_if_exists(result, '_restoreStdout')
- try:
- case.doModuleCleanups()
- except Exception as e:
- self._createClassOrModuleLevelException(result, e,
- 'tearDownModule',
- previousModule)
+ finally:
+ _call_if_exists(result, '_restoreStdout')
def _tearDownPreviousClass(self, test, result):
previousClass = getattr(result, '_previousTestClass', None)
currentClass = test.__class__
- if currentClass == previousClass:
+ if currentClass == previousClass or previousClass is None:
return
if getattr(previousClass, '_classSetupFailed', False):
return
@@ -284,27 +295,34 @@ def _tearDownPreviousClass(self, test, result):
return
tearDownClass = getattr(previousClass, 'tearDownClass', None)
- if tearDownClass is not None:
- _call_if_exists(result, '_setupStdout')
- try:
- tearDownClass()
- except Exception as e:
- if isinstance(result, _DebugResult):
- raise
- className = util.strclass(previousClass)
- self._createClassOrModuleLevelException(result, e,
- 'tearDownClass',
- className)
- finally:
- _call_if_exists(result, '_restoreStdout')
- previousClass.doClassCleanups()
- if len(previousClass.tearDown_exceptions) > 0:
- for exc in previousClass.tearDown_exceptions:
- className = util.strclass(previousClass)
- self._createClassOrModuleLevelException(result, exc[1],
- 'tearDownClass',
- className,
- info=exc)
+ doClassCleanups = getattr(previousClass, 'doClassCleanups', None)
+ if tearDownClass is None and doClassCleanups is None:
+ return
+
+ _call_if_exists(result, '_setupStdout')
+ try:
+ if tearDownClass is not None:
+ try:
+ tearDownClass()
+ except Exception as e:
+ if isinstance(result, _DebugResult):
+ raise
+ className = util.strclass(previousClass)
+ self._createClassOrModuleLevelException(result, e,
+ 'tearDownClass',
+ className)
+ if doClassCleanups is not None:
+ doClassCleanups()
+ for exc_info in previousClass.tearDown_exceptions:
+ if isinstance(result, _DebugResult):
+ raise exc_info[1]
+ className = util.strclass(previousClass)
+ self._createClassOrModuleLevelException(result, exc_info[1],
+ 'tearDownClass',
+ className,
+ info=exc_info)
+ finally:
+ _call_if_exists(result, '_restoreStdout')
class _ErrorHolder(object):
diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py
index a4af67bd8d56d..d6efc7ef0662a 100644
--- a/Lib/unittest/test/test_result.py
+++ b/Lib/unittest/test/test_result.py
@@ -2,10 +2,11 @@
import sys
import textwrap
-from test.support import warnings_helper
+from test.support import warnings_helper, captured_stdout, captured_stderr
import traceback
import unittest
+from unittest.util import strclass
class MockTraceback(object):
@@ -22,6 +23,16 @@ def restore_traceback():
unittest.result.traceback = traceback
+def bad_cleanup1():
+ print('do cleanup1')
+ raise TypeError('bad cleanup1')
+
+
+def bad_cleanup2():
+ print('do cleanup2')
+ raise ValueError('bad cleanup2')
+
+
class Test_TestResult(unittest.TestCase):
# Note: there are not separate tests for TestResult.wasSuccessful(),
# TestResult.errors, TestResult.failures, TestResult.testsRun or
@@ -633,36 +644,320 @@ def testBufferOutputAddErrorOrFailure(self):
self.assertEqual(result._original_stderr.getvalue(), expectedErrMessage)
self.assertMultiLineEqual(message, expectedFullMessage)
+ def testBufferSetUp(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ def setUp(self):
+ print('set up')
+ 1/0
+ def test_foo(self):
+ pass
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\nset up\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 1)
+ description = f'test_foo ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferTearDown(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ def tearDown(self):
+ print('tear down')
+ 1/0
+ def test_foo(self):
+ pass
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\ntear down\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 1)
+ description = f'test_foo ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferDoCleanups(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ def setUp(self):
+ print('set up')
+ self.addCleanup(bad_cleanup1)
+ self.addCleanup(bad_cleanup2)
+ def test_foo(self):
+ pass
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\nset up\ndo cleanup2\ndo cleanup1\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 2)
+ description = f'test_foo ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('ValueError: bad cleanup2', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+ test_case, formatted_exc = result.errors[1]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('TypeError: bad cleanup1', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferSetUp_DoCleanups(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ def setUp(self):
+ print('set up')
+ self.addCleanup(bad_cleanup1)
+ self.addCleanup(bad_cleanup2)
+ 1/0
+ def test_foo(self):
+ pass
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\nset up\ndo cleanup2\ndo cleanup1\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 3)
+ description = f'test_foo ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+ test_case, formatted_exc = result.errors[1]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('ValueError: bad cleanup2', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+ test_case, formatted_exc = result.errors[2]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('TypeError: bad cleanup1', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferTearDown_DoCleanups(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ def setUp(self):
+ print('set up')
+ self.addCleanup(bad_cleanup1)
+ self.addCleanup(bad_cleanup2)
+ def tearDown(self):
+ print('tear down')
+ 1/0
+ def test_foo(self):
+ pass
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\nset up\ntear down\ndo cleanup2\ndo cleanup1\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 3)
+ description = f'test_foo ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+ test_case, formatted_exc = result.errors[1]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('ValueError: bad cleanup2', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+ test_case, formatted_exc = result.errors[2]
+ self.assertEqual(str(test_case), description)
+ self.assertIn('TypeError: bad cleanup1', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
def testBufferSetupClass(self):
- result = unittest.TestResult()
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
result.buffer = True
class Foo(unittest.TestCase):
@classmethod
def setUpClass(cls):
+ print('set up class')
1/0
def test_foo(self):
pass
suite = unittest.TestSuite([Foo('test_foo')])
suite(result)
+ expected_out = '\nStdout:\nset up class\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
self.assertEqual(len(result.errors), 1)
+ description = f'setUpClass ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
def testBufferTearDownClass(self):
- result = unittest.TestResult()
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
result.buffer = True
class Foo(unittest.TestCase):
@classmethod
def tearDownClass(cls):
+ print('tear down class')
1/0
def test_foo(self):
pass
suite = unittest.TestSuite([Foo('test_foo')])
suite(result)
+ expected_out = '\nStdout:\ntear down class\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
self.assertEqual(len(result.errors), 1)
+ description = f'tearDownClass ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferDoClassCleanups(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ print('set up class')
+ cls.addClassCleanup(bad_cleanup1)
+ cls.addClassCleanup(bad_cleanup2)
+ @classmethod
+ def tearDownClass(cls):
+ print('tear down class')
+ def test_foo(self):
+ pass
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\ntear down class\ndo cleanup2\ndo cleanup1\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 2)
+ description = f'tearDownClass ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ValueError: bad cleanup2', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+ test_case, formatted_exc = result.errors[1]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('TypeError: bad cleanup1', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferSetupClass_DoClassCleanups(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ print('set up class')
+ cls.addClassCleanup(bad_cleanup1)
+ cls.addClassCleanup(bad_cleanup2)
+ 1/0
+ def test_foo(self):
+ pass
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\nset up class\ndo cleanup2\ndo cleanup1\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 3)
+ description = f'setUpClass ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn('\nStdout:\nset up class\n', formatted_exc)
+ test_case, formatted_exc = result.errors[1]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ValueError: bad cleanup2', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+ test_case, formatted_exc = result.errors[2]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('TypeError: bad cleanup1', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferTearDownClass_DoClassCleanups(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ print('set up class')
+ cls.addClassCleanup(bad_cleanup1)
+ cls.addClassCleanup(bad_cleanup2)
+ @classmethod
+ def tearDownClass(cls):
+ print('tear down class')
+ 1/0
+ def test_foo(self):
+ pass
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\ntear down class\ndo cleanup2\ndo cleanup1\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 3)
+ description = f'tearDownClass ({strclass(Foo)})'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn('\nStdout:\ntear down class\n', formatted_exc)
+ test_case, formatted_exc = result.errors[1]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ValueError: bad cleanup2', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+ test_case, formatted_exc = result.errors[2]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('TypeError: bad cleanup1', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
def testBufferSetUpModule(self):
- result = unittest.TestResult()
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
result.buffer = True
class Foo(unittest.TestCase):
@@ -671,6 +966,7 @@ def test_foo(self):
class Module(object):
@staticmethod
def setUpModule():
+ print('set up module')
1/0
Foo.__module__ = 'Module'
@@ -678,10 +974,18 @@ def setUpModule():
self.addCleanup(sys.modules.pop, 'Module')
suite = unittest.TestSuite([Foo('test_foo')])
suite(result)
+ expected_out = '\nStdout:\nset up module\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
self.assertEqual(len(result.errors), 1)
+ description = 'setUpModule (Module)'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
def testBufferTearDownModule(self):
- result = unittest.TestResult()
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
result.buffer = True
class Foo(unittest.TestCase):
@@ -690,6 +994,7 @@ def test_foo(self):
class Module(object):
@staticmethod
def tearDownModule():
+ print('tear down module')
1/0
Foo.__module__ = 'Module'
@@ -697,7 +1002,124 @@ def tearDownModule():
self.addCleanup(sys.modules.pop, 'Module')
suite = unittest.TestSuite([Foo('test_foo')])
suite(result)
+ expected_out = '\nStdout:\ntear down module\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 1)
+ description = 'tearDownModule (Module)'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferDoModuleCleanups(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ def test_foo(self):
+ pass
+ class Module(object):
+ @staticmethod
+ def setUpModule():
+ print('set up module')
+ unittest.addModuleCleanup(bad_cleanup1)
+ unittest.addModuleCleanup(bad_cleanup2)
+
+ Foo.__module__ = 'Module'
+ sys.modules['Module'] = Module
+ self.addCleanup(sys.modules.pop, 'Module')
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\ndo cleanup2\ndo cleanup1\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
self.assertEqual(len(result.errors), 1)
+ description = 'tearDownModule (Module)'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ValueError: bad cleanup2', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferSetUpModule_DoModuleCleanups(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ def test_foo(self):
+ pass
+ class Module(object):
+ @staticmethod
+ def setUpModule():
+ print('set up module')
+ unittest.addModuleCleanup(bad_cleanup1)
+ unittest.addModuleCleanup(bad_cleanup2)
+ 1/0
+
+ Foo.__module__ = 'Module'
+ sys.modules['Module'] = Module
+ self.addCleanup(sys.modules.pop, 'Module')
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\nset up module\ndo cleanup2\ndo cleanup1\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 2)
+ description = 'setUpModule (Module)'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn('\nStdout:\nset up module\n', formatted_exc)
+ test_case, formatted_exc = result.errors[1]
+ self.assertIn(expected_out, formatted_exc)
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ValueError: bad cleanup2', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
+
+ def testBufferTearDownModule_DoModuleCleanups(self):
+ with captured_stdout() as stdout:
+ result = unittest.TestResult()
+ result.buffer = True
+
+ class Foo(unittest.TestCase):
+ def test_foo(self):
+ pass
+ class Module(object):
+ @staticmethod
+ def setUpModule():
+ print('set up module')
+ unittest.addModuleCleanup(bad_cleanup1)
+ unittest.addModuleCleanup(bad_cleanup2)
+ @staticmethod
+ def tearDownModule():
+ print('tear down module')
+ 1/0
+
+ Foo.__module__ = 'Module'
+ sys.modules['Module'] = Module
+ self.addCleanup(sys.modules.pop, 'Module')
+ suite = unittest.TestSuite([Foo('test_foo')])
+ suite(result)
+ expected_out = '\nStdout:\ntear down module\ndo cleanup2\ndo cleanup1\n'
+ self.assertEqual(stdout.getvalue(), expected_out)
+ self.assertEqual(len(result.errors), 2)
+ description = 'tearDownModule (Module)'
+ test_case, formatted_exc = result.errors[0]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ZeroDivisionError: division by zero', formatted_exc)
+ self.assertNotIn('ValueError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn('\nStdout:\ntear down module\n', formatted_exc)
+ test_case, formatted_exc = result.errors[1]
+ self.assertEqual(test_case.description, description)
+ self.assertIn('ValueError: bad cleanup2', formatted_exc)
+ self.assertNotIn('ZeroDivisionError', formatted_exc)
+ self.assertNotIn('TypeError', formatted_exc)
+ self.assertIn(expected_out, formatted_exc)
if __name__ == '__main__':
diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py
index dd9a1b6d9aedd..453e6c3d11cfa 100644
--- a/Lib/unittest/test/test_runner.py
+++ b/Lib/unittest/test/test_runner.py
@@ -222,14 +222,42 @@ def tearDownClass(cls):
self.assertEqual(ordering,
['setUpClass', 'test', 'tearDownClass', 'cleanup_good'])
- def test_debug_executes_classCleanUp(self):
+ def test_run_class_cleanUp_without_tearDownClass(self):
ordering = []
+ blowUp = True
class TestableTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
ordering.append('setUpClass')
cls.addClassCleanup(cleanup, ordering)
+ if blowUp:
+ raise Exception()
+ def testNothing(self):
+ ordering.append('test')
+ @classmethod
+ @property
+ def tearDownClass(cls):
+ raise AttributeError
+
+ runTests(TestableTest)
+ self.assertEqual(ordering, ['setUpClass', 'cleanup_good'])
+
+ ordering = []
+ blowUp = False
+ runTests(TestableTest)
+ self.assertEqual(ordering,
+ ['setUpClass', 'test', 'cleanup_good'])
+
+ def test_debug_executes_classCleanUp(self):
+ ordering = []
+ blowUp = False
+
+ class TestableTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ ordering.append('setUpClass')
+ cls.addClassCleanup(cleanup, ordering, blowUp=blowUp)
def testNothing(self):
ordering.append('test')
@classmethod
@@ -241,6 +269,48 @@ def tearDownClass(cls):
self.assertEqual(ordering,
['setUpClass', 'test', 'tearDownClass', 'cleanup_good'])
+ ordering = []
+ blowUp = True
+ suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
+ with self.assertRaises(Exception) as cm:
+ suite.debug()
+ self.assertEqual(str(cm.exception), 'CleanUpExc')
+ self.assertEqual(ordering,
+ ['setUpClass', 'test', 'tearDownClass', 'cleanup_exc'])
+
+ def test_debug_executes_classCleanUp_when_teardown_exception(self):
+ ordering = []
+ blowUp = False
+
+ class TestableTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ ordering.append('setUpClass')
+ cls.addClassCleanup(cleanup, ordering, blowUp=blowUp)
+ def testNothing(self):
+ ordering.append('test')
+ @classmethod
+ def tearDownClass(cls):
+ raise Exception('TearDownClassExc')
+
+ suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
+ with self.assertRaises(Exception) as cm:
+ suite.debug()
+ self.assertEqual(str(cm.exception), 'TearDownClassExc')
+ self.assertEqual(ordering, ['setUpClass', 'test'])
+ self.assertTrue(TestableTest._class_cleanups)
+ TestableTest._class_cleanups.clear()
+
+ ordering = []
+ blowUp = True
+ suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
+ with self.assertRaises(Exception) as cm:
+ suite.debug()
+ self.assertEqual(str(cm.exception), 'TearDownClassExc')
+ self.assertEqual(ordering, ['setUpClass', 'test'])
+ self.assertTrue(TestableTest._class_cleanups)
+ TestableTest._class_cleanups.clear()
+
def test_doClassCleanups_with_errors_addClassCleanUp(self):
class TestableTest(unittest.TestCase):
def testNothing(self):
@@ -332,6 +402,7 @@ def tearDownClass(cls):
self.assertEqual(ordering,
['setUpClass', 'setUp', 'test',
'tearDownClass', 'cleanup_exc'])
+
ordering = []
class_blow_up = True
method_blow_up = False
@@ -355,6 +426,26 @@ def tearDownClass(cls):
['setUpClass', 'setUp', 'tearDownClass',
'cleanup_exc'])
+ def test_with_errors_in_tearDownClass(self):
+ ordering = []
+ class TestableTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ ordering.append('setUpClass')
+ cls.addClassCleanup(cleanup, ordering)
+ def testNothing(self):
+ ordering.append('test')
+ @classmethod
+ def tearDownClass(cls):
+ ordering.append('tearDownClass')
+ raise Exception('TearDownExc')
+
+ result = runTests(TestableTest)
+ self.assertEqual(result.errors[0][1].splitlines()[-1],
+ 'Exception: TearDownExc')
+ self.assertEqual(ordering,
+ ['setUpClass', 'test', 'tearDownClass', 'cleanup_good'])
+
class TestModuleCleanUp(unittest.TestCase):
def test_add_and_do_ModuleCleanup(self):
@@ -532,13 +623,69 @@ def tearDownClass(cls):
'tearDownModule2', 'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
- def test_debug_module_executes_cleanUp(self):
+ def test_run_module_cleanUp_without_teardown(self):
ordering = []
class Module(object):
@staticmethod
def setUpModule():
ordering.append('setUpModule')
unittest.addModuleCleanup(cleanup, ordering)
+
+ class TestableTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ ordering.append('setUpClass')
+ def testNothing(self):
+ ordering.append('test')
+ @classmethod
+ def tearDownClass(cls):
+ ordering.append('tearDownClass')
+
+ TestableTest.__module__ = 'Module'
+ sys.modules['Module'] = Module
+ runTests(TestableTest)
+ self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
+ 'tearDownClass', 'cleanup_good'])
+ self.assertEqual(unittest.case._module_cleanups, [])
+
+ def test_run_module_cleanUp_when_teardown_exception(self):
+ ordering = []
+ class Module(object):
+ @staticmethod
+ def setUpModule():
+ ordering.append('setUpModule')
+ unittest.addModuleCleanup(cleanup, ordering)
+ @staticmethod
+ def tearDownModule():
+ raise Exception('CleanUpExc')
+
+ class TestableTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ ordering.append('setUpClass')
+ def testNothing(self):
+ ordering.append('test')
+ @classmethod
+ def tearDownClass(cls):
+ ordering.append('tearDownClass')
+
+ TestableTest.__module__ = 'Module'
+ sys.modules['Module'] = Module
+ result = runTests(TestableTest)
+ self.assertEqual(result.errors[0][1].splitlines()[-1],
+ 'Exception: CleanUpExc')
+ self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
+ 'tearDownClass', 'cleanup_good'])
+ self.assertEqual(unittest.case._module_cleanups, [])
+
+ def test_debug_module_executes_cleanUp(self):
+ ordering = []
+ blowUp = False
+ class Module(object):
+ @staticmethod
+ def setUpModule():
+ ordering.append('setUpModule')
+ unittest.addModuleCleanup(cleanup, ordering, blowUp=blowUp)
@staticmethod
def tearDownModule():
ordering.append('tearDownModule')
@@ -562,6 +709,60 @@ def tearDownClass(cls):
'tearDownModule', 'cleanup_good'])
self.assertEqual(unittest.case._module_cleanups, [])
+ ordering = []
+ blowUp = True
+ suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
+ with self.assertRaises(Exception) as cm:
+ suite.debug()
+ self.assertEqual(str(cm.exception), 'CleanUpExc')
+ self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
+ 'tearDownClass', 'tearDownModule', 'cleanup_exc'])
+ self.assertEqual(unittest.case._module_cleanups, [])
+
+ def test_debug_module_cleanUp_when_teardown_exception(self):
+ ordering = []
+ blowUp = False
+ class Module(object):
+ @staticmethod
+ def setUpModule():
+ ordering.append('setUpModule')
+ unittest.addModuleCleanup(cleanup, ordering, blowUp=blowUp)
+ @staticmethod
+ def tearDownModule():
+ raise Exception('TearDownModuleExc')
+
+ class TestableTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ ordering.append('setUpClass')
+ def testNothing(self):
+ ordering.append('test')
+ @classmethod
+ def tearDownClass(cls):
+ ordering.append('tearDownClass')
+
+ TestableTest.__module__ = 'Module'
+ sys.modules['Module'] = Module
+ suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
+ with self.assertRaises(Exception) as cm:
+ suite.debug()
+ self.assertEqual(str(cm.exception), 'TearDownModuleExc')
+ self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
+ 'tearDownClass'])
+ self.assertTrue(unittest.case._module_cleanups)
+ unittest.case._module_cleanups.clear()
+
+ ordering = []
+ blowUp = True
+ suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestableTest)
+ with self.assertRaises(Exception) as cm:
+ suite.debug()
+ self.assertEqual(str(cm.exception), 'TearDownModuleExc')
+ self.assertEqual(ordering, ['setUpModule', 'setUpClass', 'test',
+ 'tearDownClass'])
+ self.assertTrue(unittest.case._module_cleanups)
+ unittest.case._module_cleanups.clear()
+
def test_addClassCleanup_arg_errors(self):
cleanups = []
def cleanup(*args, **kwargs):
@@ -717,9 +918,9 @@ def tearDownClass(cls):
method_blow_up = False
result = runTests(TestableTest)
self.assertEqual(result.errors[0][1].splitlines()[-1],
- 'Exception: CleanUpExc')
- self.assertEqual(result.errors[1][1].splitlines()[-1],
'Exception: ModuleExc')
+ self.assertEqual(result.errors[1][1].splitlines()[-1],
+ 'Exception: CleanUpExc')
self.assertEqual(ordering, ['setUpModule', 'cleanup_exc'])
ordering = []
diff --git a/Misc/NEWS.d/next/Library/2021-08-27-23-40-51.bpo-43913.Uo1Gt5.rst b/Misc/NEWS.d/next/Library/2021-08-27-23-40-51.bpo-43913.Uo1Gt5.rst
new file mode 100644
index 0000000000000..cf3d5ee0e456f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-08-27-23-40-51.bpo-43913.Uo1Gt5.rst
@@ -0,0 +1,8 @@
+Fix bugs in cleaning up classes and modules in :mod:`unittest`:
+
+* Functions registered with :func:`~unittest.addModuleCleanup` were not called unless the user defines ``tearDownModule()`` in their test module.
+* Functions registered with :meth:`~unittest.TestCase.addClassCleanup` were not called if ``tearDownClass`` is set to ``None``.
+* Buffering in :class:`~unittest.TestResult` did not work with functions registered with ``addClassCleanup()`` and ``addModuleCleanup()``.
+* Errors in functions registered with ``addClassCleanup()`` and ``addModuleCleanup()`` were not handled correctly in buffered and debug modes.
+* Errors in ``setUpModule()`` and functions registered with ``addModuleCleanup()`` were reported in wrong order.
+* And several lesser bugs.
1
0
Aug. 30, 2021
https://github.com/python/cpython/commit/720aef48b558e68c07937f0cc8d62a60f2…
commit: 720aef48b558e68c07937f0cc8d62a60f23dcb3d
branch: 3.9
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: miss-islington <31488909+miss-islington(a)users.noreply.github.com>
date: 2021-08-30T06:56:03-07:00
summary:
bpo-44449: faulthandler don't modify frame refcnt (GH-27850)
Fix a crash in the signal handler of the faulthandler module: no
longer modify the reference count of frame objects.
(cherry picked from commit fe997e1a67835a929705c8c305d41c4d7dd326e3)
Co-authored-by: Victor Stinner <vstinner(a)python.org>
files:
A Misc/NEWS.d/next/Library/2021-08-20-11-30-52.bpo-44449.1r2-lS.rst
M Python/traceback.c
diff --git a/Misc/NEWS.d/next/Library/2021-08-20-11-30-52.bpo-44449.1r2-lS.rst b/Misc/NEWS.d/next/Library/2021-08-20-11-30-52.bpo-44449.1r2-lS.rst
new file mode 100644
index 00000000000000..52f01541fca6a7
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-08-20-11-30-52.bpo-44449.1r2-lS.rst
@@ -0,0 +1,2 @@
+Fix a crash in the signal handler of the :mod:`faulthandler` module: no
+longer modify the reference count of frame objects. Patch by Victor Stinner.
diff --git a/Python/traceback.c b/Python/traceback.c
index a9a9dd92e38ee9..83f3074f43d050 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -799,7 +799,10 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
PUTS(fd, "Stack (most recent call first):\n");
}
- frame = PyThreadState_GetFrame(tstate);
+ // Use a borrowed reference. Avoid Py_INCREF/Py_DECREF, since this function
+ // can be called in a signal handler by the faulthandler module which must
+ // not modify Python objects.
+ frame = tstate->frame;
if (frame == NULL) {
PUTS(fd, "<no Python frame>\n");
return;
@@ -808,17 +811,14 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
depth = 0;
while (1) {
if (MAX_FRAME_DEPTH <= depth) {
- Py_DECREF(frame);
PUTS(fd, " ...\n");
break;
}
if (!PyFrame_Check(frame)) {
- Py_DECREF(frame);
break;
}
dump_frame(fd, frame);
- PyFrameObject *back = PyFrame_GetBack(frame);
- Py_DECREF(frame);
+ PyFrameObject *back = frame->f_back;
if (back == NULL) {
break;
1
0
bpo-41620: TestCase.run() now always return a TestResult instance (GH-28030)
by miss-islington Aug. 30, 2021
by miss-islington Aug. 30, 2021
Aug. 30, 2021
https://github.com/python/cpython/commit/243b8de0b15061704581974c0a27db1232…
commit: 243b8de0b15061704581974c0a27db1232a43b93
branch: 3.10
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: miss-islington <31488909+miss-islington(a)users.noreply.github.com>
date: 2021-08-30T06:43:38-07:00
summary:
bpo-41620: TestCase.run() now always return a TestResult instance (GH-28030)
Previously it returned None if the test class or method was
decorated with a skipping decorator.
Co-authored-by: Iman Tabrizian <iman.tabrizian(a)gmail.com>
(cherry picked from commit 7e246a3a7b43762480ee4fe0cfb859e8e997a8c8)
Co-authored-by: Serhiy Storchaka <storchaka(a)gmail.com>
files:
A Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst
M Lib/unittest/case.py
M Lib/unittest/test/test_skipping.py
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index d8522c02996f5f..e8d9c05b023378 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -573,7 +573,7 @@ def run(self, result=None):
skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
or getattr(testMethod, '__unittest_skip_why__', ''))
self._addSkip(result, self, skip_why)
- return
+ return result
expecting_failure = (
getattr(self, "__unittest_expecting_failure__", False) or
diff --git a/Lib/unittest/test/test_skipping.py b/Lib/unittest/test/test_skipping.py
index 3adde41b04e737..79b27e6daae255 100644
--- a/Lib/unittest/test/test_skipping.py
+++ b/Lib/unittest/test/test_skipping.py
@@ -14,14 +14,16 @@ def test_skip_me(self):
events = []
result = LoggingResult(events)
test = Foo("test_skip_me")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
self.assertEqual(result.skipped, [(test, "skip")])
events = []
- test.run()
+ result = test.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test, "skip")])
+ self.assertEqual(result.testsRun, 1)
# Try letting setUp skip the test now.
class Foo(unittest.TestCase):
@@ -33,15 +35,17 @@ def test_nothing(self): pass
events = []
result = LoggingResult(events)
test = Foo("test_nothing")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(result.testsRun, 1)
events = []
- test.run()
+ result = test.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test, "testing")])
+ self.assertEqual(result.testsRun, 1)
def test_skipping_subtests(self):
class Foo(unittest.TestCase):
@@ -56,7 +60,7 @@ def test_skip_me(self):
events = []
result = LoggingResult(events)
test = Foo("test_skip_me")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'addSkip',
'addSkip', 'stopTest'])
self.assertEqual(len(result.skipped), 3)
@@ -71,10 +75,12 @@ def test_skip_me(self):
self.assertEqual(result.skipped[2], (test, "skip 3"))
events = []
- test.run()
+ result = test.run()
self.assertEqual(events,
['startTestRun', 'startTest', 'addSkip', 'addSkip',
'addSkip', 'stopTest', 'stopTestRun'])
+ self.assertEqual([msg for subtest, msg in result.skipped],
+ ['skip 1', 'skip 2', 'skip 3'])
def test_skipping_decorators(self):
op_table = ((unittest.skipUnless, False, True),
@@ -95,7 +101,7 @@ def test_dont_skip(self): pass
suite = unittest.TestSuite([test_do_skip, test_dont_skip])
events = []
result = LoggingResult(events)
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(len(result.skipped), 1)
expected = ['startTest', 'addSkip', 'stopTest',
'startTest', 'addSuccess', 'stopTest']
@@ -105,16 +111,16 @@ def test_dont_skip(self): pass
self.assertTrue(result.wasSuccessful())
events = []
- test_do_skip.run()
- self.assertEqual(len(result.skipped), 1)
+ result = test_do_skip.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test_do_skip, "testing")])
events = []
- test_dont_skip.run()
- self.assertEqual(len(result.skipped), 1)
+ result = test_dont_skip.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSuccess',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [])
def test_skip_class(self):
@unittest.skip("testing")
@@ -128,15 +134,16 @@ def test_1(self):
result = LoggingResult(events)
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(record, [])
events = []
- test.run()
+ result = test.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(record, [])
def test_skip_non_unittest_class(self):
@@ -150,7 +157,7 @@ class Foo(Mixin, unittest.TestCase):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(record, [])
@@ -162,7 +169,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addExpectedFailure', 'stopTest'])
self.assertEqual(result.expectedFailures[0][0], test)
@@ -177,7 +184,7 @@ def test_1(self):
events = []
result = LoggingResult(events)
test = Foo("test_1")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addExpectedFailure', 'stopTest'])
self.assertEqual(result.expectedFailures[0][0], test)
@@ -195,7 +202,7 @@ class Bar(Foo):
events = []
result = LoggingResult(events)
test = Bar("test_1")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addExpectedFailure', 'stopTest'])
self.assertEqual(result.expectedFailures[0][0], test)
@@ -218,7 +225,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addSubTestSuccess',
'addExpectedFailure', 'stopTest'])
@@ -234,7 +241,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addUnexpectedSuccess', 'stopTest'])
self.assertFalse(result.failures)
@@ -256,7 +263,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest',
'addSubTestSuccess', 'addSubTestSuccess',
@@ -280,7 +287,7 @@ def test_1(self):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "testing")])
self.assertFalse(Foo.wasSetUp)
self.assertFalse(Foo.wasTornDown)
@@ -300,7 +307,7 @@ def test_1(self):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "testing")])
def test_skip_without_reason(self):
@@ -312,7 +319,7 @@ def test_1(self):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "")])
if __name__ == "__main__":
diff --git a/Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst b/Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst
new file mode 100644
index 00000000000000..7674d4c9532a27
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst
@@ -0,0 +1,3 @@
+:meth:`~unittest.TestCase.run` now always return a
+:class:`~unittest.TestResult` instance. Previously it returned ``None`` if
+the test class or method was decorated with a skipping decorator.
1
0
bpo-41620: TestCase.run() now always return a TestResult instance (GH-28030) (GH-28065)
by ambv Aug. 30, 2021
by ambv Aug. 30, 2021
Aug. 30, 2021
https://github.com/python/cpython/commit/0c5e0aa73f6f70d16d21ad9e1eb2d0fe3a…
commit: 0c5e0aa73f6f70d16d21ad9e1eb2d0fe3a334f0a
branch: 3.9
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: ambv <lukasz(a)langa.pl>
date: 2021-08-30T15:42:34+02:00
summary:
bpo-41620: TestCase.run() now always return a TestResult instance (GH-28030) (GH-28065)
Previously it returned None if the test class or method was
decorated with a skipping decorator.
Co-authored-by: Iman Tabrizian <iman.tabrizian(a)gmail.com>
(cherry picked from commit 7e246a3a7b43762480ee4fe0cfb859e8e997a8c8)
Co-authored-by: Serhiy Storchaka <storchaka(a)gmail.com>
files:
A Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst
M Lib/unittest/case.py
M Lib/unittest/test/test_skipping.py
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index 325e2d4a7b0262..735da3750b7f4b 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -574,7 +574,7 @@ def run(self, result=None):
skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
or getattr(testMethod, '__unittest_skip_why__', ''))
self._addSkip(result, self, skip_why)
- return
+ return result
expecting_failure = (
getattr(self, "__unittest_expecting_failure__", False) or
diff --git a/Lib/unittest/test/test_skipping.py b/Lib/unittest/test/test_skipping.py
index 3adde41b04e737..79b27e6daae255 100644
--- a/Lib/unittest/test/test_skipping.py
+++ b/Lib/unittest/test/test_skipping.py
@@ -14,14 +14,16 @@ def test_skip_me(self):
events = []
result = LoggingResult(events)
test = Foo("test_skip_me")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
self.assertEqual(result.skipped, [(test, "skip")])
events = []
- test.run()
+ result = test.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test, "skip")])
+ self.assertEqual(result.testsRun, 1)
# Try letting setUp skip the test now.
class Foo(unittest.TestCase):
@@ -33,15 +35,17 @@ def test_nothing(self): pass
events = []
result = LoggingResult(events)
test = Foo("test_nothing")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(result.testsRun, 1)
events = []
- test.run()
+ result = test.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test, "testing")])
+ self.assertEqual(result.testsRun, 1)
def test_skipping_subtests(self):
class Foo(unittest.TestCase):
@@ -56,7 +60,7 @@ def test_skip_me(self):
events = []
result = LoggingResult(events)
test = Foo("test_skip_me")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'addSkip',
'addSkip', 'stopTest'])
self.assertEqual(len(result.skipped), 3)
@@ -71,10 +75,12 @@ def test_skip_me(self):
self.assertEqual(result.skipped[2], (test, "skip 3"))
events = []
- test.run()
+ result = test.run()
self.assertEqual(events,
['startTestRun', 'startTest', 'addSkip', 'addSkip',
'addSkip', 'stopTest', 'stopTestRun'])
+ self.assertEqual([msg for subtest, msg in result.skipped],
+ ['skip 1', 'skip 2', 'skip 3'])
def test_skipping_decorators(self):
op_table = ((unittest.skipUnless, False, True),
@@ -95,7 +101,7 @@ def test_dont_skip(self): pass
suite = unittest.TestSuite([test_do_skip, test_dont_skip])
events = []
result = LoggingResult(events)
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(len(result.skipped), 1)
expected = ['startTest', 'addSkip', 'stopTest',
'startTest', 'addSuccess', 'stopTest']
@@ -105,16 +111,16 @@ def test_dont_skip(self): pass
self.assertTrue(result.wasSuccessful())
events = []
- test_do_skip.run()
- self.assertEqual(len(result.skipped), 1)
+ result = test_do_skip.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test_do_skip, "testing")])
events = []
- test_dont_skip.run()
- self.assertEqual(len(result.skipped), 1)
+ result = test_dont_skip.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSuccess',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [])
def test_skip_class(self):
@unittest.skip("testing")
@@ -128,15 +134,16 @@ def test_1(self):
result = LoggingResult(events)
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(record, [])
events = []
- test.run()
+ result = test.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(record, [])
def test_skip_non_unittest_class(self):
@@ -150,7 +157,7 @@ class Foo(Mixin, unittest.TestCase):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(record, [])
@@ -162,7 +169,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addExpectedFailure', 'stopTest'])
self.assertEqual(result.expectedFailures[0][0], test)
@@ -177,7 +184,7 @@ def test_1(self):
events = []
result = LoggingResult(events)
test = Foo("test_1")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addExpectedFailure', 'stopTest'])
self.assertEqual(result.expectedFailures[0][0], test)
@@ -195,7 +202,7 @@ class Bar(Foo):
events = []
result = LoggingResult(events)
test = Bar("test_1")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addExpectedFailure', 'stopTest'])
self.assertEqual(result.expectedFailures[0][0], test)
@@ -218,7 +225,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addSubTestSuccess',
'addExpectedFailure', 'stopTest'])
@@ -234,7 +241,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addUnexpectedSuccess', 'stopTest'])
self.assertFalse(result.failures)
@@ -256,7 +263,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest',
'addSubTestSuccess', 'addSubTestSuccess',
@@ -280,7 +287,7 @@ def test_1(self):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "testing")])
self.assertFalse(Foo.wasSetUp)
self.assertFalse(Foo.wasTornDown)
@@ -300,7 +307,7 @@ def test_1(self):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "testing")])
def test_skip_without_reason(self):
@@ -312,7 +319,7 @@ def test_1(self):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "")])
if __name__ == "__main__":
diff --git a/Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst b/Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst
new file mode 100644
index 00000000000000..7674d4c9532a27
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst
@@ -0,0 +1,3 @@
+:meth:`~unittest.TestCase.run` now always return a
+:class:`~unittest.TestResult` instance. Previously it returned ``None`` if
+the test class or method was decorated with a skipping decorator.
1
0
Aug. 30, 2021
https://github.com/python/cpython/commit/fe997e1a67835a929705c8c305d41c4d7d…
commit: fe997e1a67835a929705c8c305d41c4d7dd326e3
branch: 3.10
author: Victor Stinner <vstinner(a)python.org>
committer: vstinner <vstinner(a)python.org>
date: 2021-08-30T15:24:39+02:00
summary:
bpo-44449: faulthandler don't modify frame refcnt (GH-27850)
Fix a crash in the signal handler of the faulthandler module: no
longer modify the reference count of frame objects.
files:
A Misc/NEWS.d/next/Library/2021-08-20-11-30-52.bpo-44449.1r2-lS.rst
M Python/traceback.c
diff --git a/Misc/NEWS.d/next/Library/2021-08-20-11-30-52.bpo-44449.1r2-lS.rst b/Misc/NEWS.d/next/Library/2021-08-20-11-30-52.bpo-44449.1r2-lS.rst
new file mode 100644
index 00000000000000..52f01541fca6a7
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-08-20-11-30-52.bpo-44449.1r2-lS.rst
@@ -0,0 +1,2 @@
+Fix a crash in the signal handler of the :mod:`faulthandler` module: no
+longer modify the reference count of frame objects. Patch by Victor Stinner.
diff --git a/Python/traceback.c b/Python/traceback.c
index f7dc5ad6864762..88ffc01c6eca3a 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -796,7 +796,10 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
PUTS(fd, "Stack (most recent call first):\n");
}
- frame = PyThreadState_GetFrame(tstate);
+ // Use a borrowed reference. Avoid Py_INCREF/Py_DECREF, since this function
+ // can be called in a signal handler by the faulthandler module which must
+ // not modify Python objects.
+ frame = tstate->frame;
if (frame == NULL) {
PUTS(fd, "<no Python frame>\n");
return;
@@ -805,17 +808,14 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
depth = 0;
while (1) {
if (MAX_FRAME_DEPTH <= depth) {
- Py_DECREF(frame);
PUTS(fd, " ...\n");
break;
}
if (!PyFrame_Check(frame)) {
- Py_DECREF(frame);
break;
}
dump_frame(fd, frame);
- PyFrameObject *back = PyFrame_GetBack(frame);
- Py_DECREF(frame);
+ PyFrameObject *back = frame->f_back;
if (back == NULL) {
break;
1
0
[3.10] bpo-45007: Update to OpenSSL 1.1.1l in Windows build and CI (GH-28009) (GH-28039)
by ambv Aug. 30, 2021
by ambv Aug. 30, 2021
Aug. 30, 2021
https://github.com/python/cpython/commit/52bdda50d7b79e1ad7e57967850beff64c…
commit: 52bdda50d7b79e1ad7e57967850beff64c3ede92
branch: 3.10
author: Łukasz Langa <lukasz(a)langa.pl>
committer: ambv <lukasz(a)langa.pl>
date: 2021-08-30T15:19:16+02:00
summary:
[3.10] bpo-45007: Update to OpenSSL 1.1.1l in Windows build and CI (GH-28009) (GH-28039)
(cherry picked from commit d3bdbbf9a4352a24fc2bfc7a63a024b244b61aba)
Co-authored-by: Steve Dower <steve.dower(a)python.org>
files:
A Misc/NEWS.d/next/Windows/2021-08-27-23-50-02.bpo-45007.NIBlVG.rst
M .azure-pipelines/ci.yml
M .azure-pipelines/pr.yml
M .github/workflows/build.yml
M PCbuild/get_externals.bat
M PCbuild/python.props
diff --git a/.azure-pipelines/ci.yml b/.azure-pipelines/ci.yml
index fd7b9c6f635ff6..2284a5fb36b195 100644
--- a/.azure-pipelines/ci.yml
+++ b/.azure-pipelines/ci.yml
@@ -1,14 +1,14 @@
variables:
coverage: false
-trigger: ['master', '3.10', '3.9', '3.8', '3.7']
+trigger: ['main', '3.10', '3.9', '3.8', '3.7']
jobs:
- job: Prebuild
displayName: Pre-build checks
pool:
- vmImage: ubuntu-18.04
+ vmImage: ubuntu-20.04
steps:
- template: ./prebuild-checks.yml
@@ -20,7 +20,7 @@ jobs:
condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true'))
pool:
- vmImage: ubuntu-18.04
+ vmImage: ubuntu-20.04
steps:
- template: ./docs-steps.yml
@@ -40,7 +40,7 @@ jobs:
testRunPlatform: macos
pool:
- vmImage: macos-10.14
+ vmImage: macos-10.15
steps:
- template: ./macos-steps.yml
@@ -52,12 +52,12 @@ jobs:
condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true'))
pool:
- vmImage: ubuntu-18.04
+ vmImage: ubuntu-20.04
variables:
testRunTitle: '$(build.sourceBranchName)-linux'
testRunPlatform: linux
- openssl_version: 1.1.1k
+ openssl_version: 1.1.1l
steps:
- template: ./posix-steps.yml
@@ -78,12 +78,12 @@ jobs:
)
pool:
- vmImage: ubuntu-18.04
+ vmImage: ubuntu-20.04
variables:
testRunTitle: '$(Build.SourceBranchName)-linux-coverage'
testRunPlatform: linux-coverage
- openssl_version: 1.1.1k
+ openssl_version: 1.1.1l
steps:
- template: ./posix-steps.yml
diff --git a/.azure-pipelines/pr.yml b/.azure-pipelines/pr.yml
index a5840b73c8cb11..1a3bf75ed4c1ea 100644
--- a/.azure-pipelines/pr.yml
+++ b/.azure-pipelines/pr.yml
@@ -1,14 +1,14 @@
variables:
coverage: false
-pr: ['master', '3.10', '3.9', '3.8', '3.7']
+pr: ['main', '3.10', '3.9', '3.8', '3.7']
jobs:
- job: Prebuild
displayName: Pre-build checks
pool:
- vmImage: ubuntu-18.04
+ vmImage: ubuntu-20.04
steps:
- template: ./prebuild-checks.yml
@@ -20,7 +20,7 @@ jobs:
condition: and(succeeded(), eq(dependencies.Prebuild.outputs['docs.run'], 'true'))
pool:
- vmImage: ubuntu-18.04
+ vmImage: ubuntu-20.04
steps:
- template: ./docs-steps.yml
@@ -38,7 +38,7 @@ jobs:
testRunPlatform: macos
pool:
- vmImage: macos-10.14
+ vmImage: macos-10.15
steps:
- template: ./macos-steps.yml
@@ -52,12 +52,12 @@ jobs:
condition: and(succeeded(), eq(dependencies.Prebuild.outputs['tests.run'], 'true'))
pool:
- vmImage: ubuntu-18.04
+ vmImage: ubuntu-20.04
variables:
testRunTitle: '$(system.pullRequest.TargetBranch)-linux'
testRunPlatform: linux
- openssl_version: 1.1.1k
+ openssl_version: 1.1.1l
steps:
- template: ./posix-steps.yml
@@ -78,12 +78,12 @@ jobs:
)
pool:
- vmImage: ubuntu-18.04
+ vmImage: ubuntu-20.04
variables:
testRunTitle: '$(Build.SourceBranchName)-linux-coverage'
testRunPlatform: linux-coverage
- openssl_version: 1.1.1k
+ openssl_version: 1.1.1l
steps:
- template: ./posix-steps.yml
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 2ebb9e006dccfa..7b6ec726b651ea 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -160,7 +160,7 @@ jobs:
needs: check_source
if: needs.check_source.outputs.run_tests == 'true'
env:
- OPENSSL_VER: 1.1.1k
+ OPENSSL_VER: 1.1.1l
PYTHONSTRICTEXTENSIONBUILD: 1
steps:
- uses: actions/checkout@v2
@@ -204,7 +204,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- openssl_ver: [1.1.1k, 3.0.0-beta1]
+ openssl_ver: [1.1.1l, 3.0.0-beta1]
env:
OPENSSL_VER: ${{ matrix.openssl_ver }}
MULTISSL_DIR: ${{ github.workspace }}/multissl
diff --git a/Misc/NEWS.d/next/Windows/2021-08-27-23-50-02.bpo-45007.NIBlVG.rst b/Misc/NEWS.d/next/Windows/2021-08-27-23-50-02.bpo-45007.NIBlVG.rst
new file mode 100644
index 00000000000000..fa076ee4c8b8ad
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2021-08-27-23-50-02.bpo-45007.NIBlVG.rst
@@ -0,0 +1 @@
+Update to OpenSSL 1.1.1l in Windows build
diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat
index 8a1ab21d743c71..ea85d98e12bbd1 100644
--- a/PCbuild/get_externals.bat
+++ b/PCbuild/get_externals.bat
@@ -53,7 +53,7 @@ echo.Fetching external libraries...
set libraries=
set libraries=%libraries% bzip2-1.0.6
if NOT "%IncludeLibffiSrc%"=="false" set libraries=%libraries% libffi-3.3.0
-if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1k
+if NOT "%IncludeSSLSrc%"=="false" set libraries=%libraries% openssl-1.1.1l
set libraries=%libraries% sqlite-3.35.5.0
if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tcl-core-8.6.11.0
if NOT "%IncludeTkinterSrc%"=="false" set libraries=%libraries% tk-8.6.11.0
@@ -77,7 +77,7 @@ echo.Fetching external binaries...
set binaries=
if NOT "%IncludeLibffi%"=="false" set binaries=%binaries% libffi-3.3.0
-if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1k-1
+if NOT "%IncludeSSL%"=="false" set binaries=%binaries% openssl-bin-1.1.1l
if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.11.0
if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06
diff --git a/PCbuild/python.props b/PCbuild/python.props
index da65df58e227d9..42c67de4afa343 100644
--- a/PCbuild/python.props
+++ b/PCbuild/python.props
@@ -63,8 +63,8 @@
<libffiDir>$(ExternalsDir)libffi-3.3.0\</libffiDir>
<libffiOutDir>$(ExternalsDir)libffi-3.3.0\$(ArchName)\</libffiOutDir>
<libffiIncludeDir>$(libffiOutDir)include</libffiIncludeDir>
- <opensslDir>$(ExternalsDir)openssl-1.1.1k\</opensslDir>
- <opensslOutDir>$(ExternalsDir)openssl-bin-1.1.1k-1\$(ArchName)\</opensslOutDir>
+ <opensslDir>$(ExternalsDir)openssl-1.1.1l\</opensslDir>
+ <opensslOutDir>$(ExternalsDir)openssl-bin-1.1.1l\$(ArchName)\</opensslOutDir>
<opensslIncludeDir>$(opensslOutDir)include</opensslIncludeDir>
<nasmDir>$(ExternalsDir)\nasm-2.11.06\</nasmDir>
<zlibDir>$(ExternalsDir)\zlib-1.2.11\</zlibDir>
1
0
Aug. 30, 2021
https://github.com/python/cpython/commit/7e246a3a7b43762480ee4fe0cfb859e8e9…
commit: 7e246a3a7b43762480ee4fe0cfb859e8e997a8c8
branch: main
author: Serhiy Storchaka <storchaka(a)gmail.com>
committer: ambv <lukasz(a)langa.pl>
date: 2021-08-30T15:16:25+02:00
summary:
bpo-41620: TestCase.run() now always return a TestResult instance (GH-28030)
Previously it returned None if the test class or method was
decorated with a skipping decorator.
Co-authored-by: Iman Tabrizian <iman.tabrizian(a)gmail.com>
files:
A Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst
M Lib/unittest/case.py
M Lib/unittest/test/test_skipping.py
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py
index 8775ba9e241c44..2d193eb144a23d 100644
--- a/Lib/unittest/case.py
+++ b/Lib/unittest/case.py
@@ -575,7 +575,7 @@ def run(self, result=None):
skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
or getattr(testMethod, '__unittest_skip_why__', ''))
self._addSkip(result, self, skip_why)
- return
+ return result
expecting_failure = (
getattr(self, "__unittest_expecting_failure__", False) or
diff --git a/Lib/unittest/test/test_skipping.py b/Lib/unittest/test/test_skipping.py
index 3adde41b04e737..79b27e6daae255 100644
--- a/Lib/unittest/test/test_skipping.py
+++ b/Lib/unittest/test/test_skipping.py
@@ -14,14 +14,16 @@ def test_skip_me(self):
events = []
result = LoggingResult(events)
test = Foo("test_skip_me")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
self.assertEqual(result.skipped, [(test, "skip")])
events = []
- test.run()
+ result = test.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test, "skip")])
+ self.assertEqual(result.testsRun, 1)
# Try letting setUp skip the test now.
class Foo(unittest.TestCase):
@@ -33,15 +35,17 @@ def test_nothing(self): pass
events = []
result = LoggingResult(events)
test = Foo("test_nothing")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(result.testsRun, 1)
events = []
- test.run()
+ result = test.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test, "testing")])
+ self.assertEqual(result.testsRun, 1)
def test_skipping_subtests(self):
class Foo(unittest.TestCase):
@@ -56,7 +60,7 @@ def test_skip_me(self):
events = []
result = LoggingResult(events)
test = Foo("test_skip_me")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'addSkip',
'addSkip', 'stopTest'])
self.assertEqual(len(result.skipped), 3)
@@ -71,10 +75,12 @@ def test_skip_me(self):
self.assertEqual(result.skipped[2], (test, "skip 3"))
events = []
- test.run()
+ result = test.run()
self.assertEqual(events,
['startTestRun', 'startTest', 'addSkip', 'addSkip',
'addSkip', 'stopTest', 'stopTestRun'])
+ self.assertEqual([msg for subtest, msg in result.skipped],
+ ['skip 1', 'skip 2', 'skip 3'])
def test_skipping_decorators(self):
op_table = ((unittest.skipUnless, False, True),
@@ -95,7 +101,7 @@ def test_dont_skip(self): pass
suite = unittest.TestSuite([test_do_skip, test_dont_skip])
events = []
result = LoggingResult(events)
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(len(result.skipped), 1)
expected = ['startTest', 'addSkip', 'stopTest',
'startTest', 'addSuccess', 'stopTest']
@@ -105,16 +111,16 @@ def test_dont_skip(self): pass
self.assertTrue(result.wasSuccessful())
events = []
- test_do_skip.run()
- self.assertEqual(len(result.skipped), 1)
+ result = test_do_skip.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test_do_skip, "testing")])
events = []
- test_dont_skip.run()
- self.assertEqual(len(result.skipped), 1)
+ result = test_dont_skip.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSuccess',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [])
def test_skip_class(self):
@unittest.skip("testing")
@@ -128,15 +134,16 @@ def test_1(self):
result = LoggingResult(events)
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(events, ['startTest', 'addSkip', 'stopTest'])
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(record, [])
events = []
- test.run()
+ result = test.run()
self.assertEqual(events, ['startTestRun', 'startTest', 'addSkip',
'stopTest', 'stopTestRun'])
+ self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(record, [])
def test_skip_non_unittest_class(self):
@@ -150,7 +157,7 @@ class Foo(Mixin, unittest.TestCase):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "testing")])
self.assertEqual(record, [])
@@ -162,7 +169,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addExpectedFailure', 'stopTest'])
self.assertEqual(result.expectedFailures[0][0], test)
@@ -177,7 +184,7 @@ def test_1(self):
events = []
result = LoggingResult(events)
test = Foo("test_1")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addExpectedFailure', 'stopTest'])
self.assertEqual(result.expectedFailures[0][0], test)
@@ -195,7 +202,7 @@ class Bar(Foo):
events = []
result = LoggingResult(events)
test = Bar("test_1")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addExpectedFailure', 'stopTest'])
self.assertEqual(result.expectedFailures[0][0], test)
@@ -218,7 +225,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addSubTestSuccess',
'addExpectedFailure', 'stopTest'])
@@ -234,7 +241,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest', 'addUnexpectedSuccess', 'stopTest'])
self.assertFalse(result.failures)
@@ -256,7 +263,7 @@ def test_die(self):
events = []
result = LoggingResult(events)
test = Foo("test_die")
- test.run(result)
+ self.assertIs(test.run(result), result)
self.assertEqual(events,
['startTest',
'addSubTestSuccess', 'addSubTestSuccess',
@@ -280,7 +287,7 @@ def test_1(self):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "testing")])
self.assertFalse(Foo.wasSetUp)
self.assertFalse(Foo.wasTornDown)
@@ -300,7 +307,7 @@ def test_1(self):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "testing")])
def test_skip_without_reason(self):
@@ -312,7 +319,7 @@ def test_1(self):
result = unittest.TestResult()
test = Foo("test_1")
suite = unittest.TestSuite([test])
- suite.run(result)
+ self.assertIs(suite.run(result), result)
self.assertEqual(result.skipped, [(test, "")])
if __name__ == "__main__":
diff --git a/Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst b/Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst
new file mode 100644
index 00000000000000..7674d4c9532a27
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-08-29-14-49-22.bpo-41620.WJ6PFL.rst
@@ -0,0 +1,3 @@
+:meth:`~unittest.TestCase.run` now always return a
+:class:`~unittest.TestResult` instance. Previously it returned ``None`` if
+the test class or method was decorated with a skipping decorator.
1
0
[3.8] bpo-45007: Update multissl to openssl 1.1.1l as well (GH-28044) (GH-28063)
by ambv Aug. 30, 2021
by ambv Aug. 30, 2021
Aug. 30, 2021
https://github.com/python/cpython/commit/8371290753bed7fbb939a51ea7ff0991ea…
commit: 8371290753bed7fbb939a51ea7ff0991ea32a89c
branch: 3.8
author: Christian Heimes <christian(a)python.org>
committer: ambv <lukasz(a)langa.pl>
date: 2021-08-30T15:08:12+02:00
summary:
[3.8] bpo-45007: Update multissl to openssl 1.1.1l as well (GH-28044) (GH-28063)
This was missed while upgrading CI..
(cherry picked from commit d6cb5dd9e19210f5963ff8beadde7ca2fda71574)
Co-authored-by: Łukasz Langa <lukasz(a)langa.pl>
Co-authored-by: Łukasz Langa <lukasz(a)langa.pl>
files:
M Tools/ssl/multissltests.py
diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py
index 1904f3bf25bee2..41d964ec3ff14f 100755
--- a/Tools/ssl/multissltests.py
+++ b/Tools/ssl/multissltests.py
@@ -49,8 +49,8 @@
]
OPENSSL_RECENT_VERSIONS = [
- "1.1.1k",
- # "3.0.0-alpha14"
+ "1.1.1l",
+ "3.0.0-beta1"
]
LIBRESSL_OLD_VERSIONS = [
1
0
[3.9] bpo-45007: Update multissl to openssl 1.1.1l as well (GH-28044) (GH-28062)
by ambv Aug. 30, 2021
by ambv Aug. 30, 2021
Aug. 30, 2021
https://github.com/python/cpython/commit/576803d7a8083cb25c58be08d3cb31e05d…
commit: 576803d7a8083cb25c58be08d3cb31e05d5d2f8a
branch: 3.9
author: Christian Heimes <christian(a)python.org>
committer: ambv <lukasz(a)langa.pl>
date: 2021-08-30T15:07:55+02:00
summary:
[3.9] bpo-45007: Update multissl to openssl 1.1.1l as well (GH-28044) (GH-28062)
This was missed while upgrading CI..
(cherry picked from commit d6cb5dd9e19210f5963ff8beadde7ca2fda71574)
Co-authored-by: Łukasz Langa <lukasz(a)langa.pl>
files:
M Tools/ssl/multissltests.py
diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py
index a9c4f6dacd4499..6baec7bc31a581 100755
--- a/Tools/ssl/multissltests.py
+++ b/Tools/ssl/multissltests.py
@@ -49,8 +49,8 @@
]
OPENSSL_RECENT_VERSIONS = [
- "1.1.1k",
- "3.0.0-alpha16"
+ "1.1.1l",
+ "3.0.0-beta1"
]
LIBRESSL_OLD_VERSIONS = [
1
0
Aug. 30, 2021
https://github.com/python/cpython/commit/dc46f4c9846ad16567ab293b405efc3ed1…
commit: dc46f4c9846ad16567ab293b405efc3ed10ab9d5
branch: 3.10
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: miss-islington <31488909+miss-islington(a)users.noreply.github.com>
date: 2021-08-30T06:07:38-07:00
summary:
bpo-45007: Update multissl to openssl 1.1.1l as well (GH-28044)
This was missed while upgrading CI.
(cherry picked from commit d6cb5dd9e19210f5963ff8beadde7ca2fda71574)
Co-authored-by: Łukasz Langa <lukasz(a)langa.pl>
files:
M Tools/ssl/multissltests.py
diff --git a/Tools/ssl/multissltests.py b/Tools/ssl/multissltests.py
index 25ce2faffacfc3..9b46c8c7c09d5c 100755
--- a/Tools/ssl/multissltests.py
+++ b/Tools/ssl/multissltests.py
@@ -47,7 +47,7 @@
]
OPENSSL_RECENT_VERSIONS = [
- "1.1.1k",
+ "1.1.1l",
"3.0.0-beta1"
]
1
0