From 43e74c73bc64d2a1d7b9a640d0537f68e293687b Mon Sep 17 00:00:00 2001 From: Timofey Ivankov Date: Mon, 24 Aug 2026 22:26:28 +0300 Subject: [PATCH 1/2] gh-156327: Fix asyncio.print_call_graph() reporting its own frame --- Lib/asyncio/graph.py | 3 ++- Lib/test/test_asyncio/test_graph.py | 10 +++++++++- .../2026-08-24-22-20-55.gh-issue-156327.arC8Ph.rst | 2 ++ 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-08-24-22-20-55.gh-issue-156327.arC8Ph.rst diff --git a/Lib/asyncio/graph.py b/Lib/asyncio/graph.py index 7e85e08c4291a0..a2b673089af60d 100644 --- a/Lib/asyncio/graph.py +++ b/Lib/asyncio/graph.py @@ -273,4 +273,5 @@ def print_call_graph( limit: int | None = None, ) -> None: """Print the async call graph for the current task or the provided Future.""" - print(format_call_graph(future, depth=depth, limit=limit), file=file) + # gh-156327: print_call_graph() must not report its own frame + print(format_call_graph(future, depth=depth + 1, limit=limit), file=file) diff --git a/Lib/test/test_asyncio/test_graph.py b/Lib/test/test_asyncio/test_graph.py index 928b618fe5c55b..490f42c5b75938 100644 --- a/Lib/test/test_asyncio/test_graph.py +++ b/Lib/test/test_asyncio/test_graph.py @@ -41,7 +41,7 @@ def walk(s): return ret buf = io.StringIO() - asyncio.print_call_graph(fut, file=buf, depth=depth+1) + asyncio.print_call_graph(fut, file=buf, depth=depth) stack = asyncio.capture_call_graph(fut, depth=depth) return walk(stack), buf.getvalue() @@ -422,6 +422,14 @@ def test_capture_call_graph_non_future(self): with self.assertRaises(TypeError): asyncio.capture_call_graph("not a future") + async def test_print_call_graph_innermost_frame(self): + # gh-156327: print_call_graph() must not report its own frame + buf = io.StringIO() + lineno = sys._getframe().f_lineno + 1 + asyncio.print_call_graph(file=buf) + first_frame = buf.getvalue().splitlines()[2] + self.assertIn(f'File {__file__!r}, line {lineno},', first_frame) + async def test_capture_call_graph_no_current_task(self): results = [] diff --git a/Misc/NEWS.d/next/Library/2026-08-24-22-20-55.gh-issue-156327.arC8Ph.rst b/Misc/NEWS.d/next/Library/2026-08-24-22-20-55.gh-issue-156327.arC8Ph.rst new file mode 100644 index 00000000000000..979db7a733a3ea --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-24-22-20-55.gh-issue-156327.arC8Ph.rst @@ -0,0 +1,2 @@ +Fix :func:`asyncio.print_call_graph` including its own frame in the printed +call stack. From 6ab6fc1ff1f63cf60b18072229769fef681aeb30 Mon Sep 17 00:00:00 2001 From: Timofey Ivankov Date: Sat, 29 Aug 2026 18:46:03 +0300 Subject: [PATCH 2/2] lint fix --- Lib/test/test_asyncio/test_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_asyncio/test_graph.py b/Lib/test/test_asyncio/test_graph.py index 9d4009b7ee7a56..50d528fb9fb2c5 100644 --- a/Lib/test/test_asyncio/test_graph.py +++ b/Lib/test/test_asyncio/test_graph.py @@ -491,7 +491,7 @@ async def test_print_call_graph_innermost_frame(self): asyncio.print_call_graph(file=buf) first_frame = buf.getvalue().splitlines()[2] self.assertIn(f'File {__file__!r}, line {lineno},', first_frame) - + async def test_call_graph_finished_task(self): # gh-156408: the call graph must not record a finished coroutine's None frame async def boom():