]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-111058: Change coro.cr_frame/gen.gi_frame to be None for a closed coroutine...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 1 Dec 2023 14:13:15 +0000 (15:13 +0100)
committerGitHub <noreply@github.com>
Fri, 1 Dec 2023 14:13:15 +0000 (14:13 +0000)
Include/internal/pycore_frame.h
Lib/test/test_coroutines.py
Lib/test/test_inspect/test_inspect.py
Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst [new file with mode: 0644]
Objects/genobject.c

index 158db2cf9df82ef7d97c437734e2cb5395518452..bfe4a759bac078f954f04403b16cc1278f8c5c1c 100644 (file)
@@ -39,6 +39,8 @@ typedef enum _framestate {
     FRAME_CLEARED = 4
 } PyFrameState;
 
+#define FRAME_STATE_FINISHED(S) ((S) >= FRAME_COMPLETED)
+
 enum _frameowner {
     FRAME_OWNED_BY_THREAD = 0,
     FRAME_OWNED_BY_GENERATOR = 1,
index 47145782c0f04f70f6cef7f78288ab446b631331..25c981d1511bc1e5a762cdc7507dd1c07917a7d5 100644 (file)
@@ -2216,6 +2216,14 @@ class CoroutineTest(unittest.TestCase):
             gen.cr_frame.clear()
         gen.close()
 
+    def test_cr_frame_after_close(self):
+        async def f():
+            pass
+        gen = f()
+        self.assertIsNotNone(gen.cr_frame)
+        gen.close()
+        self.assertIsNone(gen.cr_frame)
+
     def test_stack_in_coroutine_throw(self):
         # Regression test for https://github.com/python/cpython/issues/93592
         async def a():
index 63b15819584bbb9895edf892089335d99e7981ff..a8671f9c5bb8a1516d46eeda69f524be90878995 100644 (file)
@@ -2264,6 +2264,10 @@ class TestGetGeneratorState(unittest.TestCase):
             self.generator.throw(RuntimeError)
         self.assertEqual(self._generatorstate(), inspect.GEN_CLOSED)
 
+    def test_closed_after_close(self):
+        self.generator.close()
+        self.assertEqual(self._generatorstate(), inspect.GEN_CLOSED)
+
     def test_running(self):
         # As mentioned on issue #10220, checking for the RUNNING state only
         # makes sense inside the generator itself.
@@ -2373,6 +2377,10 @@ class TestGetCoroutineState(unittest.TestCase):
             self.coroutine.throw(RuntimeError)
         self.assertEqual(self._coroutinestate(), inspect.CORO_CLOSED)
 
+    def test_closed_after_close(self):
+        self.coroutine.close()
+        self.assertEqual(self._coroutinestate(), inspect.CORO_CLOSED)
+
     def test_easy_debugging(self):
         # repr() and str() of a coroutine state should contain the state name
         names = 'CORO_CREATED CORO_RUNNING CORO_SUSPENDED CORO_CLOSED'.split()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst
new file mode 100644 (file)
index 0000000..de5661f
--- /dev/null
@@ -0,0 +1,3 @@
+Change coro.cr_frame/gen.gi_frame to return ``None`` after the coroutine/generator has been closed.
+This fixes a bug where :func:`~inspect.getcoroutinestate` and :func:`~inspect.getgeneratorstate`
+return the wrong state for a closed coroutine/generator.
index b13b52edf5c52d306dadba3b04f9b967d197ef7d..3b9e4a6036742025f590ba03eaee9ba04d872026 100644 (file)
@@ -750,7 +750,7 @@ _gen_getframe(PyGenObject *gen, const char *const name)
     if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) {
         return NULL;
     }
-    if (gen->gi_frame_state == FRAME_CLEARED) {
+    if (FRAME_STATE_FINISHED(gen->gi_frame_state)) {
         Py_RETURN_NONE;
     }
     return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject((_PyInterpreterFrame *)gen->gi_iframe));