diff -r 5f0d1aad7322 Lib/unittest/case.py --- a/Lib/unittest/case.py Wed Dec 04 00:41:24 2013 +0100 +++ b/Lib/unittest/case.py Wed Dec 04 01:11:20 2013 +0100 @@ -69,6 +69,9 @@ class _Outcome(object): else: self.success = False self.errors.append((test_case, exc_info)) + # explicitly break a reference cycle: + # exc_info -> frame -> exc_info + exc_info = None else: if self.result_supports_subtests and self.success: self.errors.append((test_case, None)) @@ -577,6 +580,9 @@ class TestCase(object): for test, reason in outcome.skipped: self._addSkip(result, test, reason) self._feedErrorsToResult(result, outcome.errors) + # explicitly break a reference cycle: + # outcome.errors -> frame -> outcome -> outcome.errors + outcome.errors.clear() if outcome.success: if expecting_failure: if outcome.expectedFailure: diff -r 5f0d1aad7322 Lib/unittest/test/test_case.py --- a/Lib/unittest/test/test_case.py Wed Dec 04 00:41:24 2013 +0100 +++ b/Lib/unittest/test/test_case.py Wed Dec 04 01:11:20 2013 +0100 @@ -1533,6 +1533,26 @@ test case del case self.assertFalse(wr()) + def test_no_exception_leak(self): + # Issue #19880: TestCase.run() should not keep a reference + # to the exception + class MyException(Exception): + ninstance = 0 + + def __init__(self): + MyException.ninstance += 1 + Exception.__init__(self) + + def __del__(self): + MyException.ninstance -= 1 + + def raise_exc(): + raise MyException() + + test = unittest.FunctionTestCase(raise_exc) + test.run() + self.assertEqual(MyException.ninstance, 0) + if __name__ == "__main__": unittest.main()