Skip to content

Commit 2743bbe

Browse files
committed
gh-156408: Fix asyncio.print_call_graph() on a finished task
1 parent e2118b0 commit 2743bbe

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
@@ -422,6 +422,24 @@ def test_capture_call_graph_non_future(self):
422422
with self.assertRaises(TypeError):
423423
asyncio.capture_call_graph("not a future")
424424

425+
async def test_call_graph_finished_task(self):
426+
# gh-156408: the call graph must not record a finished coroutine's None frame
427+
async def boom():
428+
raise ValueError
429+
430+
done = asyncio.create_task(asyncio.sleep(0), name='done')
431+
failed = asyncio.create_task(boom(), name='failed')
432+
cancelled = asyncio.create_task(asyncio.Event().wait(), name='cancelled')
433+
cancelled.cancel()
434+
await asyncio.gather(done, failed, cancelled, return_exceptions=True)
435+
436+
for task in (done, failed, cancelled):
437+
with self.subTest(task=task.get_name()):
438+
buf = io.StringIO()
439+
asyncio.print_call_graph(task, file=buf)
440+
self.assertEqual(asyncio.capture_call_graph(task).call_stack, ())
441+
self.assertIn(f"name={task.get_name()!r}", buf.getvalue())
442+
425443
async def test_capture_call_graph_no_current_task(self):
426444
results = []
427445

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)