diff options
author | Benjamin Peterson <benjamin@python.org> | 2013-05-15 03:31:26 (GMT) |
---|---|---|
committer | Benjamin Peterson <benjamin@python.org> | 2013-05-15 03:31:26 (GMT) |
commit | e1b4cbc422efc2cc6c5bbbf26645dbb5c001cb49 (patch) | |
tree | 532371b6a46fa021141c9be584dcfab109c3114f | |
parent | d486707d2e36f3141da3a3845066e7dabfd94198 (diff) | |
download | cpython-e1b4cbc422efc2cc6c5bbbf26645dbb5c001cb49.zip cpython-e1b4cbc422efc2cc6c5bbbf26645dbb5c001cb49.tar.gz cpython-e1b4cbc422efc2cc6c5bbbf26645dbb5c001cb49.tar.bz2 |
when arguments are cells clear the locals slot (backport of #17927)
-rw-r--r-- | Lib/test/test_scope.py | 29 | ||||
-rw-r--r-- | Lib/test/test_super.py | 13 | ||||
-rw-r--r-- | Misc/NEWS | 3 | ||||
-rw-r--r-- | Objects/typeobject.c | 12 | ||||
-rw-r--r-- | Python/ceval.c | 8 |
5 files changed, 63 insertions, 2 deletions
diff --git a/Lib/test/test_scope.py b/Lib/test/test_scope.py index 129a18a..41678b5 100644 --- a/Lib/test/test_scope.py +++ b/Lib/test/test_scope.py @@ -1,4 +1,6 @@ import unittest +import weakref + from test.support import check_syntax_error, cpython_only, run_unittest @@ -713,6 +715,33 @@ class ScopeTests(unittest.TestCase): def b(): global a + @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. + class Tester: + def dig(self): + if 0: + lambda: self + try: + 1/0 + except Exception as exc: + self.exc = exc + self = None # Break the cycle + tester = Tester() + tester.dig() + ref = weakref.ref(tester) + del tester + self.assertIsNone(ref()) def test_main(): diff --git a/Lib/test/test_super.py b/Lib/test/test_super.py index f6469cf..1e272ee 100644 --- a/Lib/test/test_super.py +++ b/Lib/test/test_super.py @@ -130,6 +130,19 @@ class TestSuper(unittest.TestCase): super() self.assertRaises(RuntimeError, X().f) + def test_cell_as_self(self): + class X: + def meth(self): + super() + + def f(): + k = X() + def g(): + return k + return g + c = f().__closure__[0] + self.assertRaises(TypeError, X.meth, c) + def test_main(): support.run_unittest(TestSuper) @@ -12,6 +12,9 @@ What's New in Python 3.3.2? Core and Builtins ----------------- +- Issue #17927: Frame objects kept arguments alive if they had been copied into + a cell, even if the cell was cleared. + - Issue #17237: Fix crash in the ASCII decoder on m68k. - Issue #17408: Avoid using an obsolete instance of the copyreg module when diff --git a/Objects/typeobject.c b/Objects/typeobject.c index f40dd10..a55d977 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6519,6 +6519,18 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds) return -1; } obj = f->f_localsplus[0]; + if (obj == NULL && co->co_cell2arg) { + /* The first argument might be a cell. */ + n = PyTuple_GET_SIZE(co->co_cellvars); + for (i = 0; i < n; i++) { + if (co->co_cell2arg[i] == 0) { + PyObject *cell = f->f_localsplus[co->co_nlocals + i]; + assert(PyCell_Check(cell)); + obj = PyCell_GET(cell); + break; + } + } + } if (obj == NULL) { PyErr_SetString(PyExc_RuntimeError, "super(): arg[0] deleted"); diff --git a/Python/ceval.c b/Python/ceval.c index f427841..e59c39d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3403,10 +3403,14 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, 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 + /* Clear the local copy. */ + SETLOCAL(arg, NULL); + } + else { c = PyCell_New(NULL); + } if (c == NULL) goto fail; SETLOCAL(co->co_nlocals + i, c); |