Skip to content

Commit 6c425f1

Browse files
authored
gh-156408: Fix asyncio.print_call_graph() on a finished task (#156410)
1 parent b0c9fc3 commit 6c425f1

3 files changed

Lines changed: 22 additions & 1 deletion

File tree

Lib/asyncio/graph.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ def _build_graph_for_future(
5858
while coro is not None:
5959
if hasattr(coro, 'cr_await'):
6060
# A native coroutine or duck-type compatible iterator
61-
st.append(FrameCallGraphEntry(coro.cr_frame))
61+
if coro.cr_frame is not None:
62+
st.append(FrameCallGraphEntry(coro.cr_frame))
6263
coro = coro.cr_await
6364
elif hasattr(coro, 'ag_await'):
6465
# A native async generator or duck-type compatible iterator

Lib/test/test_asyncio/test_graph.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,24 @@ def test_capture_call_graph_non_future(self):
484484
with self.assertRaises(TypeError):
485485
asyncio.capture_call_graph("not a future")
486486

487+
async def test_call_graph_finished_task(self):
488+
# gh-156408: the call graph must not record a finished coroutine's None frame
489+
async def boom():
490+
raise ValueError
491+
492+
done = asyncio.create_task(asyncio.sleep(0), name='done')
493+
failed = asyncio.create_task(boom(), name='failed')
494+
cancelled = asyncio.create_task(asyncio.Event().wait(), name='cancelled')
495+
cancelled.cancel()
496+
await asyncio.gather(done, failed, cancelled, return_exceptions=True)
497+
498+
for task in (done, failed, cancelled):
499+
with self.subTest(task=task.get_name()):
500+
buf = io.StringIO()
501+
asyncio.print_call_graph(task, file=buf)
502+
self.assertEqual(asyncio.capture_call_graph(task).call_stack, ())
503+
self.assertIn(f"name={task.get_name()!r}", buf.getvalue())
504+
487505
async def test_capture_call_graph_no_current_task(self):
488506
results = []
489507

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :func:`asyncio.print_call_graph` raising :exc:`AttributeError` when
2+
called on a task that has already finished.

0 commit comments

Comments
 (0)