From 8df2797fe885302985550b0d570d50d28ce72a42 Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Sun, 18 Jan 2026 22:14:39 +0800 Subject: [PATCH 1/4] Fix: handle suspended state on types.coroutine wrappers --- Lib/test/test_inspect/test_inspect.py | 24 ++++++++++++++++++++++++ Lib/types.py | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 075e1802bebc3e..1536af15440575 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -2804,6 +2804,30 @@ def running_check_generator(): # Running after the first yield next(self.generator) + def test_types_coroutine_wrapper_state(self): + def gen(): + yield 1 + yield 2 + + @types.coroutine + def legacy_coro(): + # return a generator iterator so types.coroutine + # wraps it into types._GeneratorWrapper. + return gen() + + g = legacy_coro() + self.addCleanup(g.close) + self.assertIs(type(g), types._GeneratorWrapper) + + # _GeneratorWrapper must provide gi_suspended/cr_suspended + # so inspect.get*state() doesn't raise AttributeError. + self.assertEqual(inspect.getgeneratorstate(g), inspect.GEN_CREATED) + self.assertEqual(inspect.getcoroutinestate(g), inspect.CORO_CREATED) + + next(g) + self.assertEqual(inspect.getgeneratorstate(g), inspect.GEN_SUSPENDED) + self.assertEqual(inspect.getcoroutinestate(g), inspect.CORO_SUSPENDED) + def test_easy_debugging(self): # repr() and str() of a generator state should contain the state name names = 'GEN_CREATED GEN_RUNNING GEN_SUSPENDED GEN_CLOSED'.split() diff --git a/Lib/types.py b/Lib/types.py index f96c75b46daba7..73a69c40c8d4b8 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -276,10 +276,14 @@ def gi_running(self): @property def gi_yieldfrom(self): return self.__wrapped.gi_yieldfrom + @property + def gi_suspended(self): + return self.__wrapped.gi_suspended cr_code = gi_code cr_frame = gi_frame cr_running = gi_running cr_await = gi_yieldfrom + cr_suspended = gi_suspended def __next__(self): return next(self.__wrapped) def __iter__(self): From 5aa614955462fa725af9b9609398af6133bceb1f Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 18 Jan 2026 14:35:38 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst diff --git a/Misc/NEWS.d/next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst b/Misc/NEWS.d/next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst new file mode 100644 index 00000000000000..32df8eee813966 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst @@ -0,0 +1 @@ +Fix an issue where ``inspect.getgeneratorstate()`` and ``inspect.getcoroutinestate()`` could fail for generators wrapped by ``types.coroutine`` in the suspended state. From bf8c7f98df77c1d21782590ace62b0d7c5382240 Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Mon, 19 Jan 2026 06:02:21 +0800 Subject: [PATCH 3/4] Update Misc/NEWS.d/next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst Co-authored-by: AN Long --- .../next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst b/Misc/NEWS.d/next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst index 32df8eee813966..dc87411aacc821 100644 --- a/Misc/NEWS.d/next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst +++ b/Misc/NEWS.d/next/Library/2026-01-18-14-35-37.gh-issue-143999.MneN4O.rst @@ -1 +1 @@ -Fix an issue where ``inspect.getgeneratorstate()`` and ``inspect.getcoroutinestate()`` could fail for generators wrapped by ``types.coroutine`` in the suspended state. +Fix an issue where :func:`inspect.getgeneratorstate` and :func:`inspect.getcoroutinestate` could fail for generators wrapped by :func:`types.coroutine` in the suspended state. From ce1a27d7b4c718a9d7542b0e8498b8f29e638b46 Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Mon, 19 Jan 2026 19:09:35 +0800 Subject: [PATCH 4/4] Update test function name --- Lib/test/test_inspect/test_inspect.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index 1536af15440575..70fd5e5615419a 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -2810,12 +2810,12 @@ def gen(): yield 2 @types.coroutine - def legacy_coro(): + def wrapped_generator_coro(): # return a generator iterator so types.coroutine # wraps it into types._GeneratorWrapper. return gen() - g = legacy_coro() + g = wrapped_generator_coro() self.addCleanup(g.close) self.assertIs(type(g), types._GeneratorWrapper)