diff options
author | Victor Stinner <victor.stinner@gmail.com> | 2017-03-27 22:56:28 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-27 22:56:28 (GMT) |
commit | bbd3cf8f1ef1e91a8d6dac6411e18b4b9084abf5 (patch) | |
tree | 38bdb93a32d6ad08bba502f3aedda22b60b6c845 /Lib/unittest | |
parent | 6003db7db5fec545c01923c198a5fdfca5a91538 (diff) | |
download | cpython-bbd3cf8f1ef1e91a8d6dac6411e18b4b9084abf5.zip cpython-bbd3cf8f1ef1e91a8d6dac6411e18b4b9084abf5.tar.gz cpython-bbd3cf8f1ef1e91a8d6dac6411e18b4b9084abf5.tar.bz2 |
Fix ref cycles in TestCase.assertRaises() (#193)
bpo-23890: unittest.TestCase.assertRaises() now manually breaks a
reference cycle to not keep objects alive longer than expected.
Diffstat (limited to 'Lib/unittest')
-rw-r--r-- | Lib/unittest/case.py | 52 | ||||
-rw-r--r-- | Lib/unittest/test/test_case.py | 13 |
2 files changed, 43 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 |