[Python-checkins] cpython: #17927: Keep frame from referencing cell-ified arguments.
guido.van.rossum
python-checkins at python.org
Fri May 10 17:48:04 CEST 2013
http://hg.python.org/cpython/rev/d331e14cae42
changeset: 83706:d331e14cae42
user: Guido van Rossum <guido at python.org>
date: Fri May 10 08:47:42 2013 -0700
summary:
#17927: Keep frame from referencing cell-ified arguments.
files:
Lib/test/test_scope.py | 41 +++++++++++++++++++++++++++++-
Misc/NEWS | 3 ++
Objects/typeobject.c | 4 ++
Python/ceval.c | 16 ++++++++--
4 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/Lib/test/test_scope.py b/Lib/test/test_scope.py
--- a/Lib/test/test_scope.py
+++ b/Lib/test/test_scope.py
@@ -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 --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@
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 #17807: Generators can now be finalized even when they are part of
a reference cycle.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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 --git a/Python/ceval.c b/Python/ceval.c
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -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) {
--
Repository URL: http://hg.python.org/cpython
More information about the Python-checkins
mailing list