gh-90908: Document asyncio.Task.cancelling() and asyncio.Task.uncancel()#95253
Conversation
|
Can I wait until the other reviewers have done their thing? I'm kind of stressed for time right now. |
|
@gvanrossum Take your time, we'll be shaping this into a final form over the next day or two. Thanks! |
|
I'll deal with the code example separately but in general, I have this question: don't you think the ordering of documented methods on How about instead of doing:
we could do:
|
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
| .. method:: cancel(msg=None) | ||
|
|
||
| Request the Task to be cancelled. | ||
|
|
||
| This arranges for a :exc:`CancelledError` exception to be thrown | ||
| into the wrapped coroutine on the next cycle of the event loop. | ||
|
|
||
| The coroutine then has a chance to clean up or even deny the | ||
| request by suppressing the exception with a :keyword:`try` ... | ||
| ... ``except CancelledError`` ... :keyword:`finally` block. | ||
| Therefore, unlike :meth:`Future.cancel`, :meth:`Task.cancel` does | ||
| not guarantee that the Task will be cancelled, although | ||
| suppressing cancellation completely is not common and is actively | ||
| discouraged. | ||
|
|
||
| .. versionchanged:: 3.9 | ||
| Added the *msg* parameter. | ||
|
|
||
| .. deprecated-removed:: 3.11 3.14 | ||
| *msg* parameter is ambiguous when multiple :meth:`cancel` | ||
| are called with different cancellation messages. | ||
| The argument will be removed. | ||
|
|
||
| .. _asyncio_example_task_cancel: | ||
|
|
||
| The following example illustrates how coroutines can intercept | ||
| the cancellation request:: | ||
|
|
||
| async def cancel_me(): | ||
| print('cancel_me(): before sleep') | ||
|
|
||
| try: | ||
| # Wait for 1 hour | ||
| await asyncio.sleep(3600) | ||
| except asyncio.CancelledError: | ||
| print('cancel_me(): cancel sleep') | ||
| raise | ||
| finally: | ||
| print('cancel_me(): after sleep') | ||
|
|
||
| async def main(): | ||
| # Create a "cancel_me" Task | ||
| task = asyncio.create_task(cancel_me()) | ||
|
|
||
| # Wait for 1 second | ||
| await asyncio.sleep(1) | ||
|
|
||
| task.cancel() | ||
| try: | ||
| await task | ||
| except asyncio.CancelledError: | ||
| print("main(): cancel_me is cancelled now") | ||
|
|
||
| asyncio.run(main()) | ||
|
|
||
| # Expected output: | ||
| # | ||
| # cancel_me(): before sleep | ||
| # cancel_me(): cancel sleep | ||
| # cancel_me(): after sleep | ||
| # main(): cancel_me is cancelled now | ||
|
|
||
| .. method:: cancelled() | ||
|
|
||
| Return ``True`` if the Task is *cancelled*. | ||
|
|
||
| The Task is *cancelled* when the cancellation was requested with | ||
| :meth:`cancel` and the wrapped coroutine propagated the | ||
| :exc:`CancelledError` exception thrown into it. | ||
|
|
There was a problem hiding this comment.
This part is unchanged, only moved down. IMO cancellation isn't as important as getting other things out of a task. Plus this move allows us to keep cancel-specific methods next to each other, uncancel in particular being pretty low-level.
ce195cb to
0203e01
Compare
| continue running even in case of the timeout. This can be | ||
| implemented with :meth:`uncancel` as follows:: |
There was a problem hiding this comment.
I'm not sure if we need to give an implementation of a structured concurrency primitive as an example in the docs, given that we don't expect (or want!) people to do this. I also don't have time to review the example carefully enough to trust it doesn't have bugs that would be replicated if people copy this example.
There was a problem hiding this comment.
Uh oh!
There was an error while loading. Please reload this page.