diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2017-06-15 22:18:15 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-15 22:18:15 (GMT) |
commit | 50dbf577e10f806056d60ac956db0748d2cc8257 (patch) | |
tree | f28a773dacfbfee77ef7d04da81f0c4be19e2707 | |
parent | e064d4dfeda09fd206653697b70b434e98cc1b57 (diff) | |
download | cpython-50dbf577e10f806056d60ac956db0748d2cc8257.zip cpython-50dbf577e10f806056d60ac956db0748d2cc8257.tar.gz cpython-50dbf577e10f806056d60ac956db0748d2cc8257.tar.bz2 |
bpo-23890: Fix ref cycle in TestCase.assertRaises (#858)
unittest.TestCase.assertRaises() now manually breaks a
reference cycle to not keep objects alive longer than expected.
(cherry picked from commit bbd3cf8f1ef1e91a8d6dac6411e18b4b9084abf5)
-rw-r--r-- | Lib/unittest/case.py | 52 | ||||
-rw-r--r-- | Lib/unittest/test/test_case.py | 13 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
3 files changed, 46 insertions, 22 deletions
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index b523f73..f4dbc52 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -153,28 +153,32 @@ class _AssertRaisesBaseContext(_BaseTestCaseContext): If args is not empty, call a callable passing positional and keyword arguments. """ - if not _is_subtype(self.expected, self._base_type): - raise TypeError('%s() arg 1 must be %s' % - (name, self._base_type_str)) - if args and args[0] is None: - warnings.warn("callable is None", - DeprecationWarning, 3) - args = () - if not args: - self.msg = kwargs.pop('msg', None) - if kwargs: - warnings.warn('%r is an invalid keyword argument for ' - 'this function' % next(iter(kwargs)), - DeprecationWarning, 3) - return self - - callable_obj, *args = args try: - self.obj_name = callable_obj.__name__ - except AttributeError: - self.obj_name = str(callable_obj) - with self: - callable_obj(*args, **kwargs) + if not _is_subtype(self.expected, self._base_type): + raise TypeError('%s() arg 1 must be %s' % + (name, self._base_type_str)) + if args and args[0] is None: + warnings.warn("callable is None", + DeprecationWarning, 3) + args = () + if not args: + self.msg = kwargs.pop('msg', None) + if kwargs: + warnings.warn('%r is an invalid keyword argument for ' + 'this function' % next(iter(kwargs)), + DeprecationWarning, 3) + return self + + callable_obj, *args = args + try: + self.obj_name = callable_obj.__name__ + except AttributeError: + self.obj_name = str(callable_obj) + with self: + callable_obj(*args, **kwargs) + finally: + # bpo-23890: manually break a reference cycle + self = None class _AssertRaisesContext(_AssertRaisesBaseContext): @@ -725,7 +729,11 @@ class TestCase(object): self.assertEqual(the_exception.error_code, 3) """ context = _AssertRaisesContext(expected_exception, self) - return context.handle('assertRaises', args, kwargs) + try: + return context.handle('assertRaises', args, kwargs) + finally: + # bpo-23890: manually break a reference cycle + context = None def assertWarns(self, expected_warning, *args, **kwargs): """Fail unless a warning of class warnClass is triggered diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py index 8f752b8..b849591 100644 --- a/Lib/unittest/test/test_case.py +++ b/Lib/unittest/test/test_case.py @@ -1273,6 +1273,19 @@ test case with self.assertRaises(TypeError): self.assertRaises((ValueError, object)) + def testAssertRaisesRefcount(self): + # bpo-23890: assertRaises() must not keep objects alive longer + # than expected + def func() : + try: + raise ValueError + except ValueError: + raise ValueError + + refcount = sys.getrefcount(func) + self.assertRaises(ValueError, func) + self.assertEqual(refcount, sys.getrefcount(func)) + def testAssertRaisesRegex(self): class ExceptionMock(Exception): pass @@ -51,6 +51,9 @@ Core and Builtins Library ------- +- bpo-23890: unittest.TestCase.assertRaises() now manually breaks a reference + cycle to not keep objects alive longer than expected. + - bpo-30149: inspect.signature() now supports callables with variable-argument parameters wrapped with partialmethod. Patch by Dong-hee Na. |