https://github.com/python/cpython/commit/e208bf0dda73b8e5a32af154f448e68ad11b26b7
commit: e208bf0dda73b8e5a32af154f448e68ad11b26b7
branch: main
author: Timofei Ivankov <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-09-05T16:48:38Z
summary:

gh-156988: Fix asyncio call graph breaking at sync generators (#156991)

files:
A Misc/NEWS.d/next/Library/2026-09-05-18-23-30.gh-issue-156988.nEUQE6.rst
M Lib/asyncio/graph.py
M Lib/test/test_asyncio/test_graph.py

diff --git a/Lib/asyncio/graph.py b/Lib/asyncio/graph.py
index 94fcd33d7088a6..d5db59f5d6f5f3 100644
--- a/Lib/asyncio/graph.py
+++ b/Lib/asyncio/graph.py
@@ -155,7 +155,9 @@ def capture_call_graph(
     f = sys._getframe(depth) if limit != 0 else None
     try:
         while f is not None:
-            is_async = f.f_generator is not None
+            # gh-156988: sync gen should not clear the call chain
+            is_async = isinstance(
+                f.f_generator, (types.CoroutineType, types.AsyncGeneratorType))
             call_stack.append(FrameCallGraphEntry(f))
 
             if is_async:
diff --git a/Lib/test/test_asyncio/test_graph.py 
b/Lib/test/test_asyncio/test_graph.py
index 50d528fb9fb2c5..36841672e1f0f6 100644
--- a/Lib/test/test_asyncio/test_graph.py
+++ b/Lib/test/test_asyncio/test_graph.py
@@ -593,6 +593,26 @@ async def main():
         await main()
         self.assertRegex(output[0], r'in generator [\w.<>]+\.gen\(\)')
 
+    async def test_capture_call_graph_generator_keeps_caller_frames(self):
+        # gh-156988: sync gen should not clear the call chain
+        stack = None
+
+        def gen():
+            nonlocal stack
+            graph = asyncio.capture_call_graph()
+            stack = [entry.frame.f_code.co_name for entry in graph.call_stack]
+            yield
+
+        def middle():
+            for _ in gen():
+                pass
+
+        async def main():
+            middle()
+
+        await main()
+        self.assertEqual(stack[:3], ['gen', 'middle', 'main'])
+
 
 @unittest.skipIf(
     not hasattr(asyncio.futures, "_c_future_add_to_awaited_by"),
diff --git 
a/Misc/NEWS.d/next/Library/2026-09-05-18-23-30.gh-issue-156988.nEUQE6.rst 
b/Misc/NEWS.d/next/Library/2026-09-05-18-23-30.gh-issue-156988.nEUQE6.rst
new file mode 100644
index 00000000000000..6cb2f1f4cf14ae
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-09-05-18-23-30.gh-issue-156988.nEUQE6.rst
@@ -0,0 +1,2 @@
+:func:`asyncio.print_call_graph` no longer truncates the call stack at a
+synchronous generator.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to