Skip to content

gh-90908: Document asyncio.Task.cancelling() and asyncio.Task.uncancel()#95253

Merged
gvanrossum merged 12 commits into
python:mainfrom
ambv:document-asyncio-task-uncancel
Oct 1, 2022
Merged

gh-90908: Document asyncio.Task.cancelling() and asyncio.Task.uncancel()#95253
gvanrossum merged 12 commits into
python:mainfrom
ambv:document-asyncio-task-uncancel

Conversation

@ambv
Copy link
Copy Markdown
Contributor

@ambv ambv commented Jul 25, 2022

@ambv ambv requested review from graingert and gvanrossum July 25, 2022 17:38
@ambv ambv requested review from 1st1 and asvetlov as code owners July 25, 2022 17:38
@bedevere-bot bedevere-bot added awaiting core review tests Tests in the Lib/test dir labels Jul 25, 2022
@ambv ambv added needs backport to 3.11 only security fixes docs Documentation in the Doc dir labels Jul 25, 2022
@gvanrossum
Copy link
Copy Markdown
Member

Can I wait until the other reviewers have done their thing? I'm kind of stressed for time right now.

Comment thread Doc/library/asyncio-task.rst Outdated
@ambv
Copy link
Copy Markdown
Contributor Author

ambv commented Jul 25, 2022

@gvanrossum Take your time, we'll be shaping this into a final form over the next day or two. Thanks!

@ambv
Copy link
Copy Markdown
Contributor Author

ambv commented Jul 26, 2022

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 Task is somewhat awkward now?

How about instead of doing:

  • cancel()
  • cancelled()
  • cancelling()
  • uncancel()
  • done()
  • result()
  • exception()
  • add_done_callback()
  • remove_done_callback()
  • get_stack()
  • print_stack()
  • get_coro()
  • get_name()
  • set_name()

we could do:

  • done()
  • result()
  • exception()
  • add_done_callback()
  • remove_done_callback()
  • cancel()
  • cancelled()
  • cancelling()
  • uncancel()
  • get_stack()
  • print_stack()
  • get_coro()
  • get_name()
  • set_name()

Co-authored-by: Thomas Grainger <tagrain@gmail.com>
Comment on lines +1114 to +1183
.. 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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread Doc/library/asyncio-task.rst Outdated
@ambv ambv force-pushed the document-asyncio-task-uncancel branch from ce195cb to 0203e01 Compare July 27, 2022 10:58
Comment thread Doc/library/asyncio-task.rst Outdated
Comment thread Doc/library/asyncio-task.rst Outdated
Comment thread Doc/library/asyncio-task.rst Outdated
Comment thread Doc/library/asyncio-task.rst
Comment thread Doc/library/asyncio-task.rst Outdated
Comment on lines +1228 to +1229
continue running even in case of the timeout. This can be
implemented with :meth:`uncancel` as follows::
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.