https://github.com/python/cpython/commit/238efbecab24204f822b1d1611914f5bcb2ae2de
commit: 238efbecab24204f822b1d1611914f5bcb2ae2de
branch: 3.12
author: Irit Katriel <[email protected]>
committer: iritkatriel <[email protected]>
date: 2024-05-02T16:22:50+01:00
summary:

[3.12] gh-118272: Clear generator frame's locals when the generator is closed 
(#118451)

files:
A Misc/NEWS.d/next/Core and 
Builtins/2024-04-30-23-06-10.gh-issue-118272.5ptjk_.rst
M Include/internal/pycore_frame.h
M Lib/test/test_generators.py
M Objects/genobject.c
M Python/frame.c

diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index bfe4a759bac078..ad7d74c5dd2b9f 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 1ee9958445bf18..e0da9152c33954 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -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):
 
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 00000000000000..32043440fd0365
--- /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 119a71fcabf04a..dc034a4b723472 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 b84fd9b6a9380a..a49215fa44a796 100644
--- a/Python/frame.c
+++ b/Python/frame.c
@@ -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
 _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
 {
@@ -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);
 }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to