]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-148736: Use ag_frame when walking async generators in asyncio.graph (#148737)
authorRaj Kripal Danday <rajkripal.danday@gmail.com>
Sun, 26 Jul 2026 09:08:51 +0000 (02:08 -0700)
committerGitHub <noreply@github.com>
Sun, 26 Jul 2026 09:08:51 +0000 (14:38 +0530)
Lib/asyncio/graph.py
Lib/test/test_asyncio/test_graph.py
Misc/NEWS.d/next/Library/2026-04-18-12-00-00.gh-issue-148736.ag-frame.rst [new file with mode: 0644]

index 35f7fa62140bd49f36df736671f1ad8aebc6f8e7..7e85e08c4291a06956f600368a4de95b4fc48b75 100644 (file)
@@ -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
index 3dde7535284b255c871a7a600de66515a06d3e06..928b618fe5c55b7ed60fe7008d4b2767a949c713 100644 (file)
@@ -1,5 +1,6 @@
 import asyncio
 import io
+import sys
 import unittest
 from unittest import mock
 
@@ -147,6 +148,31 @@ class CallStackTestBase:
             'async generator CallStackTestBase.test_stack_async_gen.<locals>.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 (file)
index 0000000..e9701df
--- /dev/null
@@ -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``.