mirror of
https://github.com/python/cpython.git
synced 2025-10-03 05:35:59 +00:00
[3.12] gh-118272: Clear generator frame's locals when the generator is closed (#118451)
This commit is contained in:
parent
f61de0de64
commit
238efbecab
5 changed files with 39 additions and 5 deletions
|
@ -213,6 +213,9 @@ _PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
|
||||||
return _PyFrame_MakeAndSetFrameObject(frame);
|
return _PyFrame_MakeAndSetFrameObject(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyFrame_ClearLocals(_PyInterpreterFrame *frame);
|
||||||
|
|
||||||
/* Clears all references in the frame.
|
/* Clears all references in the frame.
|
||||||
* If take is non-zero, then the _PyInterpreterFrame frame
|
* If take is non-zero, then the _PyInterpreterFrame frame
|
||||||
* may be transferred to the frame object it references
|
* may be transferred to the frame object it references
|
||||||
|
|
|
@ -450,6 +450,26 @@ class ExceptionTest(unittest.TestCase):
|
||||||
self.assertIsInstance(cm.exception.value, StopIteration)
|
self.assertIsInstance(cm.exception.value, StopIteration)
|
||||||
self.assertEqual(cm.exception.value.value, 2)
|
self.assertEqual(cm.exception.value.value, 2)
|
||||||
|
|
||||||
|
def test_close_releases_frame_locals(self):
|
||||||
|
# See gh-118272
|
||||||
|
|
||||||
|
class Foo:
|
||||||
|
pass
|
||||||
|
|
||||||
|
f = Foo()
|
||||||
|
f_wr = weakref.ref(f)
|
||||||
|
|
||||||
|
def genfn():
|
||||||
|
a = f
|
||||||
|
yield
|
||||||
|
|
||||||
|
g = genfn()
|
||||||
|
next(g)
|
||||||
|
del f
|
||||||
|
g.close()
|
||||||
|
support.gc_collect()
|
||||||
|
self.assertIsNone(f_wr())
|
||||||
|
|
||||||
|
|
||||||
class GeneratorThrowTest(unittest.TestCase):
|
class GeneratorThrowTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix bug where ``generator.close`` does not free the generator frame's
|
||||||
|
locals.
|
|
@ -403,6 +403,7 @@ gen_close(PyGenObject *gen, PyObject *args)
|
||||||
* StopIteration. */
|
* StopIteration. */
|
||||||
if (exception_handler_depth == 1) {
|
if (exception_handler_depth == 1) {
|
||||||
gen->gi_frame_state = FRAME_COMPLETED;
|
gen->gi_frame_state = FRAME_COMPLETED;
|
||||||
|
_PyFrame_ClearLocals((_PyInterpreterFrame *)gen->gi_iframe);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,18 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_PyFrame_ClearLocals(_PyInterpreterFrame *frame)
|
||||||
|
{
|
||||||
|
assert(frame->stacktop >= 0);
|
||||||
|
int stacktop = frame->stacktop;
|
||||||
|
frame->stacktop = 0;
|
||||||
|
for (int i = 0; i < stacktop; i++) {
|
||||||
|
Py_XDECREF(frame->localsplus[i]);
|
||||||
|
}
|
||||||
|
Py_CLEAR(frame->f_locals);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
|
_PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
|
||||||
{
|
{
|
||||||
|
@ -135,12 +147,8 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
|
||||||
}
|
}
|
||||||
Py_DECREF(f);
|
Py_DECREF(f);
|
||||||
}
|
}
|
||||||
assert(frame->stacktop >= 0);
|
_PyFrame_ClearLocals(frame);
|
||||||
for (int i = 0; i < frame->stacktop; i++) {
|
|
||||||
Py_XDECREF(frame->localsplus[i]);
|
|
||||||
}
|
|
||||||
Py_XDECREF(frame->frame_obj);
|
Py_XDECREF(frame->frame_obj);
|
||||||
Py_XDECREF(frame->f_locals);
|
|
||||||
Py_DECREF(frame->f_funcobj);
|
Py_DECREF(frame->f_funcobj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue