diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2023-12-01 14:13:15 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-01 14:13:15 (GMT) |
commit | 7eff607debb6e749a0066ec7fd20be2616c17da3 (patch) | |
tree | ca5dcf4bdf458e5fad47703d1d599f38c7d11baf | |
parent | edce0c4fb3e5f9fb26b2c214e3479ffa13dbaa43 (diff) | |
download | cpython-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.h | 2 | ||||
-rw-r--r-- | Lib/test/test_coroutines.py | 8 | ||||
-rw-r--r-- | Lib/test/test_inspect/test_inspect.py | 8 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2023-11-26-21-30-11.gh-issue-111058.q4DqDY.rst | 3 | ||||
-rw-r--r-- | Objects/genobject.c | 2 |
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)); |