Skip to content

Commit

Permalink
Fixed asyncio CancelScope not recognizing its own cancellation exce…
Browse files Browse the repository at this point in the history
…ption

...if it comes wrapped in an exception group.
Fixes #634.
  • Loading branch information
agronholm committed Nov 22, 2023
1 parent 019835a commit 844e089
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 2 deletions.
2 changes: 2 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ This library adheres to `Semantic Versioning 2.0 <http://semver.org/>`_.
- Removed a checkpoint when exiting a task group
- Bumped minimum version of trio to v0.23
- Exposed the ``ResourceGuard`` class in the public API
- Fixed discrepancy between ``asyncio`` and ``trio`` where reraising a cancellation
exception in an ``except*`` block would incorrectly bubble out of its cancel scope

**4.0.0**

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ trio = ["trio >= 0.23"]
test = [
"anyio[trio]",
"coverage[toml] >= 7",
"exceptiongroup >= 1.2.0",
"hypothesis >= 4.0",
"psutil >= 5.9",
"pytest >= 7.0",
Expand Down
14 changes: 12 additions & 2 deletions src/anyio/_backends/_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,18 @@ def __exit__(
if self._shield:
self._deliver_cancellation_to_parent()

if isinstance(exc_val, CancelledError) and self._cancel_called:
self._cancelled_caught = self._uncancel(exc_val)
if self._cancel_called:
if isinstance(exc_val, CancelledError):
self._cancelled_caught = self._uncancel(exc_val)
elif isinstance(exc_val, BaseExceptionGroup) and (
excgrp := exc_val.split(CancelledError)[0]
):
for exc in excgrp.exceptions:
if isinstance(exc, CancelledError):
self._cancelled_caught = self._uncancel(exc)
if self._cancelled_caught:
break

return self._cancelled_caught

return None
Expand Down
11 changes: 11 additions & 0 deletions tests/test_taskgroups.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Any, NoReturn, cast

import pytest
from exceptiongroup import catch

import anyio
from anyio import (
Expand Down Expand Up @@ -1282,6 +1283,16 @@ async def test_cancel_before_entering_task_group() -> None:
pytest.fail("This should not raise a cancellation exception")


async def test_reraise_cancelled_in_excgroup() -> None:
def handler(excgrp: BaseExceptionGroup) -> None:
raise

with CancelScope() as scope:
scope.cancel()
with catch({get_cancelled_exc_class(): handler}):
await anyio.sleep_forever()


class TestTaskStatusTyping:
"""
These tests do not do anything at run time, but since the test suite is also checked
Expand Down

0 comments on commit 844e089

Please sign in to comment.