Skip to content

Commit 238efbe

Browse files
authored
[3.12] gh-118272: Clear generator frame's locals when the generator is closed (#118451)
1 parent f61de0d commit 238efbe

File tree

5 files changed

+39
-5
lines changed

5 files changed

+39
-5
lines changed

Include/internal/pycore_frame.h

+3
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ _PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
213213
return _PyFrame_MakeAndSetFrameObject(frame);
214214
}
215215

216+
void
217+
_PyFrame_ClearLocals(_PyInterpreterFrame *frame);
218+
216219
/* Clears all references in the frame.
217220
* If take is non-zero, then the _PyInterpreterFrame frame
218221
* may be transferred to the frame object it references

Lib/test/test_generators.py

+20
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,26 @@ def g():
450450
self.assertIsInstance(cm.exception.value, StopIteration)
451451
self.assertEqual(cm.exception.value.value, 2)
452452

453+
def test_close_releases_frame_locals(self):
454+
# See gh-118272
455+
456+
class Foo:
457+
pass
458+
459+
f = Foo()
460+
f_wr = weakref.ref(f)
461+
462+
def genfn():
463+
a = f
464+
yield
465+
466+
g = genfn()
467+
next(g)
468+
del f
469+
g.close()
470+
support.gc_collect()
471+
self.assertIsNone(f_wr())
472+
453473

454474
class GeneratorThrowTest(unittest.TestCase):
455475

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix bug where ``generator.close`` does not free the generator frame's
2+
locals.

Objects/genobject.c

+1
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ gen_close(PyGenObject *gen, PyObject *args)
403403
* StopIteration. */
404404
if (exception_handler_depth == 1) {
405405
gen->gi_frame_state = FRAME_COMPLETED;
406+
_PyFrame_ClearLocals((_PyInterpreterFrame *)gen->gi_iframe);
406407
Py_RETURN_NONE;
407408
}
408409
}

Python/frame.c

+13-5
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,18 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
115115
}
116116
}
117117

118+
void
119+
_PyFrame_ClearLocals(_PyInterpreterFrame *frame)
120+
{
121+
assert(frame->stacktop >= 0);
122+
int stacktop = frame->stacktop;
123+
frame->stacktop = 0;
124+
for (int i = 0; i < stacktop; i++) {
125+
Py_XDECREF(frame->localsplus[i]);
126+
}
127+
Py_CLEAR(frame->f_locals);
128+
}
129+
118130
void
119131
_PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
120132
{
@@ -135,12 +147,8 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
135147
}
136148
Py_DECREF(f);
137149
}
138-
assert(frame->stacktop >= 0);
139-
for (int i = 0; i < frame->stacktop; i++) {
140-
Py_XDECREF(frame->localsplus[i]);
141-
}
150+
_PyFrame_ClearLocals(frame);
142151
Py_XDECREF(frame->frame_obj);
143-
Py_XDECREF(frame->f_locals);
144152
Py_DECREF(frame->f_funcobj);
145153
}
146154

0 commit comments

Comments
 (0)