Skip to content

Commit 26f55f6

Browse files
committed
avoid error when coro.cr_frame is None
1 parent 84fbcb7 commit 26f55f6

File tree

3 files changed

+24
-2
lines changed

3 files changed

+24
-2
lines changed

newsfragments/3337.bugfix.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
`trio.lowlevel.Task.iter_await_frames` now works on completed tasks, by
2+
returning an empty list of frames if the underlying coroutine has been closed.
3+
Previously, it raised an internal error.

src/trio/_core/_run.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,11 +1588,13 @@ def print_stack_for_task(task):
15881588
while coro is not None:
15891589
if hasattr(coro, "cr_frame"):
15901590
# A real coroutine
1591-
yield coro.cr_frame, coro.cr_frame.f_lineno
1591+
if cr_frame := coro.cr_frame: # None if the task has finished
1592+
yield cr_frame, cr_frame.f_lineno
15921593
coro = coro.cr_await
15931594
elif hasattr(coro, "gi_frame"):
15941595
# A generator decorated with @types.coroutine
1595-
yield coro.gi_frame, coro.gi_frame.f_lineno
1596+
if gi_frame := coro.gi_frame: # pragma: no branch
1597+
yield gi_frame, gi_frame.f_lineno # pragma: no cover
15961598
coro = coro.gi_yieldfrom
15971599
elif coro.__class__.__name__ in [
15981600
"async_generator_athrow",

src/trio/_tests/test_tracing.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,20 @@ async def test_task_iter_await_frames_async_gen() -> None:
6969
]
7070

7171
nursery.cancel_scope.cancel()
72+
73+
74+
async def test_closed_task_iter_await_frames() -> None:
75+
async with trio.open_nursery() as nursery:
76+
task = object()
77+
78+
async def capture_task() -> None:
79+
nonlocal task
80+
task = trio.lowlevel.current_task()
81+
await trio.lowlevel.checkpoint()
82+
83+
nursery.start_soon(capture_task)
84+
85+
# Task has completed, so coro.cr_frame should be None, thus no frames
86+
assert isinstance(task, trio.lowlevel.Task) # Ran `capture_task`
87+
assert task.coro.cr_frame is None # and the task was over, but
88+
assert list(task.iter_await_frames()) == [] # look, no crash!

0 commit comments

Comments
 (0)