Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e1b4cbc

Browse files
committed
when arguments are cells clear the locals slot (backport of #17927)
1 parent d486707 commit e1b4cbc

5 files changed

Lines changed: 63 additions & 2 deletions

File tree

Lib/test/test_scope.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import unittest
2+
import weakref
3+
24
from test.support import check_syntax_error, cpython_only, run_unittest
35

46

@@ -713,6 +715,33 @@ def top(a):
713715
def b():
714716
global a
715717

718+
@cpython_only
719+
def testCellLeak(self):
720+
# Issue 17927.
721+
#
722+
# The issue was that if self was part of a cycle involving the
723+
# frame of a method call, *and* the method contained a nested
724+
# function referencing self, thereby forcing 'self' into a
725+
# cell, setting self to None would not be enough to break the
726+
# frame -- the frame had another reference to the instance,
727+
# which could not be cleared by the code running in the frame
728+
# (though it will be cleared when the frame is collected).
729+
# Without the lambda, setting self to None is enough to break
730+
# the cycle.
731+
class Tester:
732+
def dig(self):
733+
if 0:
734+
lambda: self
735+
try:
736+
1/0
737+
except Exception as exc:
738+
self.exc = exc
739+
self = None # Break the cycle
740+
tester = Tester()
741+
tester.dig()
742+
ref = weakref.ref(tester)
743+
del tester
744+
self.assertIsNone(ref())
716745

717746

718747
def test_main():

Lib/test/test_super.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,19 @@ def f(x):
130130
super()
131131
self.assertRaises(RuntimeError, X().f)
132132

133+
def test_cell_as_self(self):
134+
class X:
135+
def meth(self):
136+
super()
137+
138+
def f():
139+
k = X()
140+
def g():
141+
return k
142+
return g
143+
c = f().__closure__[0]
144+
self.assertRaises(TypeError, X.meth, c)
145+
133146

134147
def test_main():
135148
support.run_unittest(TestSuper)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in Python 3.3.2?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #17927: Frame objects kept arguments alive if they had been copied into
16+
a cell, even if the cell was cleared.
17+
1518
- Issue #17237: Fix crash in the ASCII decoder on m68k.
1619

1720
- Issue #17408: Avoid using an obsolete instance of the copyreg module when

Objects/typeobject.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6519,6 +6519,18 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds)
65196519
return -1;
65206520
}
65216521
obj = f->f_localsplus[0];
6522+
if (obj == NULL && co->co_cell2arg) {
6523+
/* The first argument might be a cell. */
6524+
n = PyTuple_GET_SIZE(co->co_cellvars);
6525+
for (i = 0; i < n; i++) {
6526+
if (co->co_cell2arg[i] == 0) {
6527+
PyObject *cell = f->f_localsplus[co->co_nlocals + i];
6528+
assert(PyCell_Check(cell));
6529+
obj = PyCell_GET(cell);
6530+
break;
6531+
}
6532+
}
6533+
}
65226534
if (obj == NULL) {
65236535
PyErr_SetString(PyExc_RuntimeError,
65246536
"super(): arg[0] deleted");

Python/ceval.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3403,10 +3403,14 @@ PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals,
34033403
int arg;
34043404
/* Possibly account for the cell variable being an argument. */
34053405
if (co->co_cell2arg != NULL &&
3406-
(arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG)
3406+
(arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) {
34073407
c = PyCell_New(GETLOCAL(arg));
3408-
else
3408+
/* Clear the local copy. */
3409+
SETLOCAL(arg, NULL);
3410+
}
3411+
else {
34093412
c = PyCell_New(NULL);
3413+
}
34103414
if (c == NULL)
34113415
goto fail;
34123416
SETLOCAL(co->co_nlocals + i, c);

0 commit comments

Comments
 (0)