summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-12-01 14:13:15 (GMT)
committerGitHub <noreply@github.com>2023-12-01 14:13:15 (GMT)
commit7eff607debb6e749a0066ec7fd20be2616c17da3 (patch)
treeca5dcf4bdf458e5fad47703d1d599f38c7d11baf
parentedce0c4fb3e5f9fb26b2c214e3479ffa13dbaa43 (diff)
downloadcpython-7eff607debb6e749a0066ec7fd20be2616c17da3.zip
cpython-7eff607debb6e749a0066ec7fd20be2616c17da3.tar.gz
cpython-7eff607debb6e749a0066ec7fd20be2616c17da3.tar.bz2
[3.12] gh-111058: Change coro.cr_frame/gen.gi_frame to be None for a closed coroutine/generator. (GH-112428) (#112589)
-rw-r--r--Include/internal/pycore_frame.h2
-rw-r--r--Lib/test/test_coroutines.py8
-rw-r--r--Lib/test/test_inspect/test_inspect.py8
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst3
-rw-r--r--Objects/genobject.c2
5 files changed, 22 insertions, 1 deletions
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index 158db2c..bfe4a75 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -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,
diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 4714578..25c981d 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -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():
diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py
index 63b1581..a8671f9 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -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
index 0000000..de5661f
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst
@@ -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.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index b13b52e..3b9e4a6 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -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));