Skip to content

Commit

Permalink
Raise a single Cancelled from trio TaskGroups (#593)
Browse files Browse the repository at this point in the history
  • Loading branch information
agronholm committed Jul 25, 2023
1 parent 7affa13 commit 6307392
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ This library adheres to `Semantic Versioning 2.0 <http://semver.org/>`_.
tasks were spawned and an outer cancellation scope had been cancelled before
- Ensured that exiting a ``TaskGroup`` always hits a yield point, regardless of
whether there are running child tasks to be waited on
- Task groups on all backends now raise a single cancellation exception when an outer
cancel scope is cancelled, and no exceptions other than cancellation exceptions are
raised in the group
- **BACKWARDS INCOMPATIBLE** Changes the pytest plugin to run all tests and fixtures in
the same task, allowing fixtures to set context variables for tests and other fixtures
- **BACKWARDS INCOMPATIBLE** Changed ``anyio.Path.relative_to()`` and
Expand Down
11 changes: 11 additions & 0 deletions src/anyio/_backends/_trio.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import array
import math
import socket
import sys
import types
from collections.abc import AsyncIterator, Iterable
from concurrent.futures import Future
Expand Down Expand Up @@ -62,6 +63,9 @@
from ..abc._eventloop import AsyncBackend
from ..streams.memory import MemoryObjectSendStream

if sys.version_info < (3, 11):
from exceptiongroup import BaseExceptionGroup

T = TypeVar("T")
T_Retval = TypeVar("T_Retval")
T_SockAddr = TypeVar("T_SockAddr", str, IPSockAddrType)
Expand Down Expand Up @@ -156,6 +160,13 @@ async def __aexit__(
) -> bool | None:
try:
return await self._nursery_manager.__aexit__(exc_type, exc_val, exc_tb)
except BaseExceptionGroup as exc:
_, rest = exc.split(trio.Cancelled)
if not rest:
cancelled_exc = trio.Cancelled._create() # type: ignore [attr-defined]
raise cancelled_exc from exc

raise
finally:
self._active = False

Expand Down
22 changes: 22 additions & 0 deletions tests/test_taskgroups.py
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,28 @@ async def main() -> NoReturn:
assert not caplog.messages


async def test_single_cancellation_exc() -> None:
"""
Test that only a single cancellation exception bubbles out of the task group when
case it was cancelled via an outer scope and no actual errors were raised.
"""
with CancelScope() as outer:
try:
async with create_task_group() as tg:
tg.start_soon(sleep, 5)
await wait_all_tasks_blocked()
outer.cancel()
await sleep(5)
except BaseException as exc:
if isinstance(exc, get_cancelled_exc_class()):
raise

pytest.fail(f"Raised the wrong type of exception: {exc}")
else:
pytest.fail("Did not raise a cancellation exception")


async def test_start_soon_parent_id() -> None:
root_task_id = get_current_task().id
parent_id: int | None = None
Expand Down

0 comments on commit 6307392

Please sign in to comment.