From 91b87b34195c39f14396b0285b38cea1971ea3d1 Mon Sep 17 00:00:00 2001 From: Timofey Ivankov Date: Mon, 2 Nov 2026 16:29:09 +0300 Subject: [PATCH 1/2] gh-161178: Fix missing async generator frames in Task.print_stack() --- Lib/asyncio/base_tasks.py | 6 +++- Lib/test/test_asyncio/test_tasks.py | 34 +++++++++++++++++++ ...-11-02-16-28-11.gh-issue-161178.D3-23R.rst | 2 ++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-11-02-16-28-11.gh-issue-161178.D3-23R.rst diff --git a/Lib/asyncio/base_tasks.py b/Lib/asyncio/base_tasks.py index c907b6834137325..831ef1b63704e23 100644 --- a/Lib/asyncio/base_tasks.py +++ b/Lib/asyncio/base_tasks.py @@ -42,8 +42,12 @@ def _task_get_stack(task, limit): elif hasattr(task._coro, 'ag_frame'): # case 3: async generators f = task._coro.ag_frame + # gh-161178: async gens should not be missed + elif not task.done() and hasattr(task._coro, 'aga_gen'): + # case 4: pending async generator operations + f = task._coro.aga_gen.ag_frame else: - # case 4: unknown objects + # case 5: unknown objects f = None if f is not None: while f is not None: diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index d4d3863923e79e6..277b856f57a1363 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -335,6 +335,40 @@ async def runner(): self.loop.run_until_complete(runner()) + def test_get_stack_async_generator(self): + # gh-161178 + async def gen(): + try: + yield + except ValueError: + pass + finally: + await asyncio.sleep(0) + yield + + async def runner(method, args): + agen = gen() + await anext(agen) + task = self.new_task(self.loop, getattr(agen, method)(*args)) + await asyncio.sleep(0) + frame = agen.ag_frame + stack = task.get_stack() + await task + try: + self.assertEqual(stack, [frame]) + self.assertEqual(task.get_stack(), []) + finally: + await agen.aclose() + + for method, args in ( + ('__anext__', ()), + ('asend', (None,)), + ('athrow', (ValueError(),)), + ('aclose', ()), + ): + with self.subTest(method=method): + self.loop.run_until_complete(runner(method, args)) + def test_task_repr(self): self.loop.set_debug(False) diff --git a/Misc/NEWS.d/next/Library/2026-11-02-16-28-11.gh-issue-161178.D3-23R.rst b/Misc/NEWS.d/next/Library/2026-11-02-16-28-11.gh-issue-161178.D3-23R.rst new file mode 100644 index 000000000000000..1130fbe95ba0190 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-11-02-16-28-11.gh-issue-161178.D3-23R.rst @@ -0,0 +1,2 @@ +Fix :meth:`asyncio.Task.print_stack` to include frames from async generator +operations. Patch by Timofei Ivankov From 03db5d9e9f6de4f0e78a2a43e902a9433ca270af Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Mon, 2 Nov 2026 19:25:22 +0530 Subject: [PATCH 2/2] Update Lib/asyncio/base_tasks.py --- Lib/asyncio/base_tasks.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/asyncio/base_tasks.py b/Lib/asyncio/base_tasks.py index 831ef1b63704e23..2e297c6e0802bea 100644 --- a/Lib/asyncio/base_tasks.py +++ b/Lib/asyncio/base_tasks.py @@ -42,9 +42,8 @@ def _task_get_stack(task, limit): elif hasattr(task._coro, 'ag_frame'): # case 3: async generators f = task._coro.ag_frame - # gh-161178: async gens should not be missed elif not task.done() and hasattr(task._coro, 'aga_gen'): - # case 4: pending async generator operations + # case 4: async generator's asend/athrow/aclose f = task._coro.aga_gen.ag_frame else: # case 5: unknown objects