Browse Source

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.
pull/866/head
Victor Stinner 9 years ago
committed by GitHub
parent
commit
bbd3cf8f1e
  1. 52
      Lib/unittest/case.py
  2. 13
      Lib/unittest/test/test_case.py
  3. 3
      Misc/NEWS

52
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

13
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

3
Misc/NEWS

@ -291,6 +291,9 @@ Extension Modules
Library
-------
- bpo-23890: unittest.TestCase.assertRaises() now manually breaks a reference
cycle to not keep objects alive longer than expected.
- bpo-29901: The zipapp module now supports general path-like objects, not
just pathlib.Path.

Loading…
Cancel
Save