-
Notifications
You must be signed in to change notification settings - Fork 135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add CancelScope.cancelled_caught #267
Conversation
It seems that there are no tests for cases when |
752f34c
to
c3e282f
Compare
@agronholm I added asserts for when |
481a087
to
891d5f8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some questions, and code style nits.
891d5f8
to
c46e3d9
Compare
Okay I think I've replied to or fixed everything |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some comments, and pointed out a problematic logic branch.
src/anyio/_backends/_asyncio.py
Outdated
elif self._cancel_called: | ||
self._cancelled_caught = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does it set cancelled_caught
to True
here, when it clearly didn't catch a CancelledError
? If a scope is cancelled but the cancellation exception is caught inside of it, cancelled_caught
should be False
.
Case in point:
import trio
async def task(*, task_status):
with trio.CancelScope() as scope:
task_status.started(scope)
try:
await trio.sleep_forever()
except trio.Cancelled:
pass
async def main():
async with trio.open_nursery() as nursery:
scope = await nursery.start(task)
scope.cancel()
print(scope.cancel_called)
print(scope.cancelled_caught)
trio.run(main)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added that code based on a misunderstanding of this conversation about whether cancelled_caught
is the best variable name. That code served a purpose; it fixed an error in test_cancel_cascade
where tg.cancel_scope.cancelled_caught
was False
only for the asyncio
backends.
Your comment here led me down a rabbit hole trying to understand how a misguided change had fixed a broken test case. The short story is that self.cancel_scope.__exit__
is the first thing done in TaskGroup.__aexit__
, so in something like
async def task():
await sleep(1)
async with create_task_group() as tg:
tg.start_soon(task)
tg.cancel_scope.__exit__
is called before the child task finishes. In CancelScope.__exit__
, I was deciding that the child task wouldn't throw a CancelledError
while it was still running!
I think this sequence of events causes issues in the current code too. For example:
async def task(scope):
await sleep(1)
with scope:
# No RuntimeError!
...
async with create_task_group() as tg:
tg.start_soon(task, tg.cancel_scope)
Anyways, I committed a crude fix based on the approach in Trio (gather exceptions from all child tasks, then close the cancel scope). It's still failing one of the asyncio
tests, but I think that's because the crude fix calls CancelScope.cancel
before calling CancelScope.__exit__
and we use CancelScope._cancel_called
to detect native cancellation.
I'll have more time in a couple days to polish it up, and will address your other comments then as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tried to do the same refactoring quite a few times and it always ended in failure. Let's see if your effort will fare better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I pushed an attempt at that refactoring. It wasn't too terrible, so I'm afraid I might have missed something 🙈
1d19a7d
to
ec3d18c
Compare
Do the test suites pass for you locally? |
Tests do pass locally, although until a final tweak, I was seeing issues with |
Added `CancelScope.cancelled_caught` to match the Trio API. Relevant Trio documentation [here](https://trio.readthedocs.io/en/stable/reference-core.html#trio.CancelScope.cancelled_caught). Refactored `TaskGroup.__aexit__` to match Trio's structured concurrency model. Closes agronholm#257
ec3d18c
to
2caa5b6
Compare
This PR includes changes I'm not comfortable having in the (imminent) v3.1 release, so I'm deferring this to v3.2, minimum. |
Added
CancelScope.cancelled_caught
to match the Trio API. RelevantTrio documentation
here.
Closes #257