diff --git a/Lib/asyncio/graph.py b/Lib/asyncio/graph.py index b5bfeb1630a159..76808f48867879 100644 --- a/Lib/asyncio/graph.py +++ b/Lib/asyncio/graph.py @@ -62,7 +62,7 @@ def _build_graph_for_future( coro = coro.cr_await elif hasattr(coro, 'ag_await'): # A native async generator or duck-type compatible iterator - st.append(FrameCallGraphEntry(coro.cr_frame)) + st.append(FrameCallGraphEntry(coro.ag_frame)) coro = coro.ag_await else: break diff --git a/Lib/test/test_asyncio/test_graph.py b/Lib/test/test_asyncio/test_graph.py index 2f22fbccba42bc..069c224302723b 100644 --- a/Lib/test/test_asyncio/test_graph.py +++ b/Lib/test/test_asyncio/test_graph.py @@ -1,5 +1,6 @@ import asyncio import io +import sys import unittest @@ -146,6 +147,31 @@ async def main(): 'async generator CallStackTestBase.test_stack_async_gen..gen()', stack_for_gen_nested_call[1]) + def test_ag_frame_used_for_async_generator(self): + # Regression test for gh-148736: the ag_await branch of + # _build_graph_for_future must read ag_frame, not cr_frame. + from asyncio.graph import _build_graph_for_future + + sentinel_frame = sys._getframe() + + class FakeAsyncGen: + ag_await = None + ag_frame = sentinel_frame + + class FakeCoro: + cr_frame = sentinel_frame + cr_await = FakeAsyncGen() + + loop = asyncio.new_event_loop() + try: + fut = loop.create_future() + fut.get_coro = lambda: FakeCoro() + result = _build_graph_for_future(fut) + finally: + loop.close() + + self.assertEqual(len(result.call_stack), 2) + async def test_stack_gather(self): stack_for_deep = None diff --git a/Misc/NEWS.d/next/Library/2026-04-18-12-00-00.gh-issue-148736.ag-frame.rst b/Misc/NEWS.d/next/Library/2026-04-18-12-00-00.gh-issue-148736.ag-frame.rst new file mode 100644 index 00000000000000..e9701dfaa6a48d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-04-18-12-00-00.gh-issue-148736.ag-frame.rst @@ -0,0 +1,3 @@ +Fix a latent :exc:`AttributeError` in :mod:`asyncio` call-graph capture when +walking an async generator's ``ag_await`` chain: the walker referenced +``cr_frame`` instead of ``ag_frame``.