[Python-checkins] GH-46771: docs for asyncio.timeout and task cancellation (GH-94794)

miss-islington webhook-mailer at python.org
Thu Jul 14 22:48:12 EDT 2022


https://github.com/python/cpython/commit/3b6cb56dc922649e6a31af962feb1941665338af
commit: 3b6cb56dc922649e6a31af962feb1941665338af
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2022-07-14T19:48:03-07:00
summary:

GH-46771: docs for asyncio.timeout and task cancellation (GH-94794)

(cherry picked from commit 08f68975abb5bc6b3205f9442b68d97f4ef044db)

Co-authored-by: Tin Tvrtković <tinchester at gmail.com>

files:
M Doc/library/asyncio-task.rst

diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index 76969762dd9f5..a307d22e456eb 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -282,6 +282,24 @@ Creating Tasks
       Added the *context* parameter.
 
 
+Task Cancellation
+=================
+
+Tasks can easily and safely be cancelled.
+When a task is cancelled, :exc:`asyncio.CancelledError` will be raised
+in the task at the next opportunity.
+
+It is recommended that coroutines use ``try/finally`` blocks to robustly
+perform clean-up logic. In case :exc:`asyncio.CancelledError`
+is explicitly caught, it should generally be propagated when
+clean-up is complete. Most code can safely ignore :exc:`asyncio.CancelledError`.
+
+Important asyncio components, like :class:`asyncio.TaskGroup` and the
+:func:`asyncio.timeout` context manager, are implemented using cancellation
+internally and might misbehave if a coroutine swallows
+:exc:`asyncio.CancelledError`.
+
+
 Task Groups
 ===========
 
@@ -537,6 +555,122 @@ Shielding From Cancellation
 Timeouts
 ========
 
+.. coroutinefunction:: timeout(delay)
+
+    An :ref:`asynchronous context manager <async-context-managers>`
+    that can be used to limit the amount of time spent waiting on
+    something.
+
+    *delay* can either be ``None``, or a float/int number of
+    seconds to wait. If *delay* is ``None``, no time limit will
+    be applied; this can be useful if the delay is unknown when
+    the context manager is created.
+
+    In either case, the context manager can be rescheduled after
+    creation using :meth:`Timeout.reschedule`.
+
+    Example::
+
+        async def main():
+            async with asyncio.timeout(10):
+                await long_running_task()
+
+    If ``long_running_task`` takes more than 10 seconds to complete,
+    the context manager will cancel the current task and handle
+    the resulting :exc:`asyncio.CancelledError` internally, transforming it
+    into an :exc:`asyncio.TimeoutError` which can be caught and handled.
+
+    .. note::
+
+      The :func:`asyncio.timeout` context manager is what transforms
+      the :exc:`asyncio.CancelledError` into an :exc:`asyncio.TimeoutError`,
+      which means the :exc:`asyncio.TimeoutError` can only be caught
+      *outside* of the context manager.
+
+    Example of catching :exc:`asyncio.TimeoutError`::
+
+        async def main():
+            try:
+                async with asyncio.timeout(10):
+                    await long_running_task()
+            except TimeoutError:
+                print("The long operation timed out, but we've handled it.")
+
+            print("This statement will run regardless.")
+
+    The context manager produced by :func:`asyncio.timeout` can be
+    rescheduled to a different deadline and inspected.
+
+    .. class:: Timeout()
+
+       An :ref:`asynchronous context manager <async-context-managers>`
+       that limits time spent inside of it.
+
+        .. versionadded:: 3.11
+
+        .. method:: when() -> float | None
+
+           Return the current deadline, or ``None`` if the current
+           deadline is not set.
+
+           The deadline is a float, consistent with the time returned by
+           :meth:`loop.time`.
+
+        .. method:: reschedule(when: float | None)
+
+            Change the time the timeout will trigger.
+
+            If *when* is `None`, any current deadline will be removed, and the
+            context manager will wait indefinitely.
+
+            If *when* is a float, it is set as the new deadline.
+
+        .. method:: expired() -> bool
+
+           Return whether the context manager has exceeded its deadline
+           (expired).
+
+    Example::
+
+        async def main():
+            try:
+                # We do not know the timeout when starting, so we pass ``None``.
+                async with asyncio.timeout(None) as cm:
+                    # We know the timeout now, so we reschedule it.
+                    new_deadline = get_running_loop().time() + 10
+                    cm.reschedule(new_deadline)
+
+                    await long_running_task()
+            except TimeoutError:
+                pass
+
+            if cm.expired:
+                print("Looks like we haven't finished on time.")
+
+    Timeout context managers can be safely nested.
+
+    .. versionadded:: 3.11
+
+.. coroutinefunction:: timeout_at(when)
+
+   Similar to :func:`asyncio.timeout`, except *when* is the absolute time
+   to stop waiting, or ``None``.
+
+   Example::
+
+      async def main():
+          loop = get_running_loop()
+          deadline = loop.time() + 20
+          try:
+              async with asyncio.timeout_at(deadline):
+                  await long_running_task()
+          except TimeoutError:
+              print("The long operation timed out, but we've handled it.")
+
+          print("This statement will run regardless.")
+
+   .. versionadded:: 3.11
+
 .. coroutinefunction:: wait_for(aw, timeout)
 
    Wait for the *aw* :ref:`awaitable <asyncio-awaitables>`



More information about the Python-checkins mailing list