]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-118272: Clear generator frame's locals when the generator is closed (#118277)
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>
Tue, 30 Apr 2024 18:32:25 +0000 (19:32 +0100)
committerGitHub <noreply@github.com>
Tue, 30 Apr 2024 18:32:25 +0000 (19:32 +0100)
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
Include/internal/pycore_frame.h
Lib/test/test_generators.py
Misc/NEWS.d/next/Core and Builtins/2024-04-25-12-55-47.gh-issue-118272.5ptjk_.rst [new file with mode: 0644]
Objects/genobject.c
Python/frame.c

index f913928f38bd0586187cb84b9e1ceda16dd1f40c..37ae5ae850389b3021c362e8abeb156901c179ee 100644 (file)
@@ -227,6 +227,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
index d48f0d47ba1962ba238c9f8c634d62059a8d7178..6d36df2c7413e0599997e4e4521af3140b364fa1 100644 (file)
@@ -532,6 +532,26 @@ class GeneratorCloseTest(unittest.TestCase):
         with self.assertRaises(RuntimeError):
             gen.close()
 
+    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-25-12-55-47.gh-issue-118272.5ptjk_.rst b/Misc/NEWS.d/next/Core and Builtins/2024-04-25-12-55-47.gh-issue-118272.5ptjk_.rst
new file mode 100644 (file)
index 0000000..3204344
--- /dev/null
@@ -0,0 +1,2 @@
+Fix bug where ``generator.close`` does not free the generator frame's
+locals.
index 5d7da49cfca1668181be990b0439c2f25d66e6d9..04736a04562e14930f26ccca6c5596b095cf1b71 100644 (file)
@@ -380,6 +380,7 @@ gen_close(PyGenObject *gen, PyObject *args)
             // RESUME after YIELD_VALUE and exception depth is 1
             assert((oparg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START);
             gen->gi_frame_state = FRAME_COMPLETED;
+            _PyFrame_ClearLocals((_PyInterpreterFrame *)gen->gi_iframe);
             Py_RETURN_NONE;
         }
     }
index db9d13359a23ca2c3842bbf357f1bf7c925fc225..ec390e7426ad4701dbe2e8e98dd285c8b0f4304f 100644 (file)
@@ -94,6 +94,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)
 {
@@ -114,11 +125,7 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
         }
         Py_DECREF(f);
     }
-    assert(frame->stacktop >= 0);
-    for (int i = 0; i < frame->stacktop; i++) {
-        Py_XDECREF(frame->localsplus[i]);
-    }
-    Py_XDECREF(frame->f_locals);
+    _PyFrame_ClearLocals(frame);
     Py_DECREF(frame->f_funcobj);
 }