diff -r c89febab4648 Lib/test/test_scope.py --- a/Lib/test/test_scope.py Wed May 08 18:12:35 2013 +0200 +++ b/Lib/test/test_scope.py Thu May 09 17:17:16 2013 -0700 @@ -1,3 +1,4 @@ +import gc import unittest from test.support import check_syntax_error, cpython_only, run_unittest @@ -713,7 +714,6 @@ def b(): global a - def testClassNamespaceOverridesClosure(self): # See #17853. x = 42 @@ -727,6 +727,45 @@ self.assertFalse(hasattr(X, "x")) self.assertEqual(x, 42) + @cpython_only + def testCellLeak(self): + # Issue 17927. + # + # The issue was that if self was part of a cycle involving the + # frame of a method call, *and* the method contained a nested + # function referencing self, thereby forcing 'self' into a + # cell, setting self to None would not be enough to break the + # frame -- the frame had another reference to the instance, + # which could not be cleared by the code running in the frame + # (though it will be cleared when the frame is collected). + # Without the lambda, setting self to None is enough to break + # the cycle. + logs = [] + class Canary: + def __del__(self): + logs.append('canary') + class Base: + def dig(self): + pass + class Tester(Base): + def __init__(self): + self.canary = Canary() + def dig(self): + if 0: + lambda: self + try: + 1/0 + except Exception as exc: + self.exc = exc + super().dig() + self = None # Break the cycle + tester = Tester() + tester.dig() + del tester + logs.append('collect') + gc.collect() + self.assertEqual(logs, ['canary', 'collect']) + def test_main(): run_unittest(ScopeTests) diff -r c89febab4648 Objects/typeobject.c --- a/Objects/typeobject.c Wed May 08 18:12:35 2013 +0200 +++ b/Objects/typeobject.c Thu May 09 17:17:16 2013 -0700 @@ -6510,6 +6510,10 @@ return -1; } obj = f->f_localsplus[0]; + if (obj != NULL && PyCell_Check(obj)) { + /* It might be a cell. See cell var initialization in ceval.c. */ + obj = PyCell_GET(obj); + } if (obj == NULL) { PyErr_SetString(PyExc_RuntimeError, "super(): arg[0] deleted"); diff -r c89febab4648 Python/ceval.c --- a/Python/ceval.c Wed May 08 18:12:35 2013 +0200 +++ b/Python/ceval.c Thu May 09 17:17:16 2013 -0700 @@ -3519,12 +3519,20 @@ int arg; /* Possibly account for the cell variable being an argument. */ if (co->co_cell2arg != NULL && - (arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) + (arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) { c = PyCell_New(GETLOCAL(arg)); - else + if (c == NULL) + goto fail; + /* Reference the cell from the argument slot, for super(). + See typeobject.c. */ + Py_INCREF(c); + SETLOCAL(arg, c); + } + else { c = PyCell_New(NULL); - if (c == NULL) - goto fail; + if (c == NULL) + goto fail; + } SETLOCAL(co->co_nlocals + i, c); } for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {