aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>2024-05-02 16:22:50 +0100
committerGitHub <noreply@github.com>2024-05-02 16:22:50 +0100
commit238efbecab24204f822b1d1611914f5bcb2ae2de (patch)
treec4ea5b0a5094613a167746ed4b140d13c2f35040
parentf61de0de649d31a96bacb7625d6a7b98d23b14bb (diff)
downloadcpython3-238efbecab24204f822b1d1611914f5bcb2ae2de.tar.gz
[3.12] gh-118272: Clear generator frame's locals when the generator is closed (#118451)
-rw-r--r--Include/internal/pycore_frame.h3
-rw-r--r--Lib/test/test_generators.py20
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2024-04-30-23-06-10.gh-issue-118272.5ptjk_.rst2
-rw-r--r--Objects/genobject.c1
-rw-r--r--Python/frame.c18
5 files changed, 39 insertions, 5 deletions
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index bfe4a759ba..ad7d74c5dd 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -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
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index 1ee9958445..e0da9152c3 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -450,6 +450,26 @@ class ExceptionTest(unittest.TestCase):
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):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-04-30-23-06-10.gh-issue-118272.5ptjk_.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-30-23-06-10.gh-issue-118272.5ptjk_.rst
new file mode 100644
index 0000000000..32043440fd
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-04-30-23-06-10.gh-issue-118272.5ptjk_.rst
@@ -0,0 +1,2 @@
+Fix bug where ``generator.close`` does not free the generator frame's
+locals.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 119a71fcab..dc034a4b72 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -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;
}
}
diff --git a/Python/frame.c b/Python/frame.c
index b84fd9b6a9..a49215fa44 100644
--- a/Python/frame.c
+++ b/Python/frame.c
@@ -116,6 +116,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
_PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
{
/* It is the responsibility of the owning generator/coroutine
@@ -135,12 +147,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);
}