diff -r c89febab4648 Lib/test/test_cellleak.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_cellleak.py Thu May 09 09:11:31 2013 -0700 @@ -0,0 +1,56 @@ +"""Tests for 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. + +NOTE: This test is specific to CPython. It depends on reference +counting. +""" + +import gc +import test.support +import unittest + + +class CellLeakTest(unittest.TestCase): + + def test_cell_leak(self): + 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(): + test.support.run_unittest(CellLeakTest) + + +if __name__ == "__main__": + test_main() 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 09:11:31 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 09:11:31 2013 -0700 @@ -3519,8 +3519,13 @@ 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)); + /* 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)