Skip to content

Commit 6e4735a

Browse files
committed
check for nursery misnesting on task exit ... not working
1 parent e590d39 commit 6e4735a

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

src/trio/_core/_run.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2017,7 +2017,7 @@ def task_exited(self, task: Task, outcome: Outcome[object]) -> None:
20172017
task._cancel_status is not None
20182018
and task._cancel_status.abandoned_by_misnesting
20192019
and task._cancel_status.parent is None
2020-
):
2020+
) or any(not nursery._closed for nursery in task._child_nurseries):
20212021
# The cancel scope surrounding this task's nursery was closed
20222022
# before the task exited. Force the task to exit with an error,
20232023
# since the error might not have been caught elsewhere. See the

src/trio/_core/_tests/test_run.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
import time
99
import types
1010
import weakref
11-
from contextlib import ExitStack, contextmanager, suppress
11+
from contextlib import (
12+
AsyncExitStack,
13+
ExitStack,
14+
asynccontextmanager,
15+
contextmanager,
16+
suppress,
17+
)
1218
from math import inf, nan
1319
from typing import TYPE_CHECKING, NoReturn, TypeVar
1420
from unittest import mock
@@ -761,7 +767,7 @@ async def enter_scope() -> None:
761767
assert scope.cancel_called # never become un-cancelled
762768

763769

764-
async def test_cancel_scope_misnesting() -> None:
770+
async def test_cancel_scope_misnesting_1() -> None:
765771
outer = _core.CancelScope()
766772
inner = _core.CancelScope()
767773
with ExitStack() as stack:
@@ -771,6 +777,8 @@ async def test_cancel_scope_misnesting() -> None:
771777
stack.close()
772778
# No further error is raised when exiting the inner context
773779

780+
781+
async def test_cancel_scope_misnesting_2() -> None:
774782
# If there are other tasks inside the abandoned part of the cancel tree,
775783
# they get cancelled when the misnesting is detected
776784
async def task1() -> None:
@@ -828,6 +836,8 @@ def no_context(exc: RuntimeError) -> bool:
828836
)
829837
assert group.matches(exc_info.value.__context__)
830838

839+
840+
async def test_cancel_scope_misnesting_3() -> None:
831841
# Trying to exit a cancel scope from an unrelated task raises an error
832842
# without affecting any state
833843
async def task3(task_status: _core.TaskStatus[_core.CancelScope]) -> None:
@@ -844,6 +854,42 @@ async def task3(task_status: _core.TaskStatus[_core.CancelScope]) -> None:
844854
scope.cancel()
845855

846856

857+
async def test_nursery_misnest() -> None:
858+
# See https://github.com/python-trio/trio/issues/3298
859+
async def inner_func() -> None:
860+
inner_nursery = await inner_cm.__aenter__()
861+
inner_nursery.start_soon(sleep, 1)
862+
863+
with pytest.RaisesGroup(
864+
pytest.RaisesExc(RuntimeError, match="Cancel scope stack corrupted")
865+
):
866+
async with _core.open_nursery() as outer_nursery:
867+
inner_cm = _core.open_nursery()
868+
outer_nursery.start_soon(inner_func)
869+
870+
871+
async def test_asyncexitstack_nursery_misnest() -> None:
872+
@asynccontextmanager
873+
async def asynccontextmanager_that_creates_a_nursery_internally() -> (
874+
AsyncGenerator[None]
875+
):
876+
async with _core.open_nursery() as nursery:
877+
nursery.start_soon(
878+
print_sleep_print, "task_in_asynccontextmanager_nursery", 2.0
879+
)
880+
yield
881+
882+
async def print_sleep_print(name: str, sleep_time: float) -> None:
883+
await sleep(sleep_time)
884+
885+
async with AsyncExitStack() as stack, _core.open_nursery() as nursery:
886+
# The asynccontextmanager is going to create a nursery that outlives this nursery!
887+
nursery.start_soon(
888+
stack.enter_async_context,
889+
asynccontextmanager_that_creates_a_nursery_internally(),
890+
)
891+
892+
847893
@slow
848894
async def test_timekeeping() -> None:
849895
# probably a good idea to use a real clock for *one* test anyway...

test-requirements.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# For tests
2-
pytest >= 5.0 # for faulthandler in core
2+
pytest >= 8.4 # for pytest.RaisesGroup
33
coverage >= 7.2.5
44
async_generator >= 1.9
55
pyright

0 commit comments

Comments
 (0)