summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2013-05-15 03:31:26 (GMT)
committerBenjamin Peterson <benjamin@python.org>2013-05-15 03:31:26 (GMT)
commite1b4cbc422efc2cc6c5bbbf26645dbb5c001cb49 (patch)
tree532371b6a46fa021141c9be584dcfab109c3114f
parentd486707d2e36f3141da3a3845066e7dabfd94198 (diff)
downloadcpython-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.py29
-rw-r--r--Lib/test/test_super.py13
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/typeobject.c12
-rw-r--r--Python/ceval.c8
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)
diff --git a/Misc/NEWS b/Misc/NEWS
index c616045..b7e749e 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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);