Skip to content

Commit

Permalink
gh-118272: Clear generator frame's locals when the generator is closed
Browse files Browse the repository at this point in the history
  • Loading branch information
iritkatriel committed Apr 30, 2024
1 parent 0586d54 commit f3515ca
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 5 deletions.
3 changes: 3 additions & 0 deletions Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ _PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
return _PyFrame_MakeAndSetFrameObject(frame);
}

void
_PyFrame_ClearLocals(_PyInterpreterFrame *frame);

/* Clears all references in the frame.
* If take is non-zero, then the _PyInterpreterFrame frame
* may be transferred to the frame object it references
Expand Down
20 changes: 20 additions & 0 deletions Lib/test/test_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,26 @@ def g():
self.assertIsInstance(cm.exception.value, StopIteration)
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):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix bug where ``generator.close`` does not free the generator frame's
locals.
1 change: 1 addition & 0 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ gen_close(PyGenObject *gen, PyObject *args)
* StopIteration. */
if (exception_handler_depth == 1) {
gen->gi_frame_state = FRAME_COMPLETED;
_PyFrame_ClearLocals((_PyInterpreterFrame *)gen->gi_iframe);
Py_RETURN_NONE;
}
}
Expand Down
17 changes: 12 additions & 5 deletions Python/frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,17 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame)
}
}

void
_PyFrame_ClearLocals(_PyInterpreterFrame *frame)
{
assert(frame->stacktop >= 0);
for (int i = 0; i < frame->stacktop; i++) {
Py_XDECREF(frame->localsplus[i]);
}
frame->stacktop = 0;
Py_CLEAR(frame->f_locals);
}

void
_PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
{
Expand All @@ -135,12 +146,8 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
}
Py_DECREF(f);
}
assert(frame->stacktop >= 0);
for (int i = 0; i < frame->stacktop; i++) {
Py_XDECREF(frame->localsplus[i]);
}
_PyFrame_ClearLocals(frame);
Py_XDECREF(frame->frame_obj);
Py_XDECREF(frame->f_locals);
Py_DECREF(frame->f_funcobj);
}

Expand Down

0 comments on commit f3515ca

Please sign in to comment.