[Python-checkins] GH-90908: Document asyncio.TaskGroup (GH-94359) (GH-94456)
ambv
webhook-mailer at python.org
Thu Jun 30 12:27:20 EDT 2022
https://github.com/python/cpython/commit/b3d69ffeb3ca2476b1921d257bc082c363cc5231
commit: b3d69ffeb3ca2476b1921d257bc082c363cc5231
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: ambv <lukasz at langa.pl>
date: 2022-06-30T18:27:15+02:00
summary:
GH-90908: Document asyncio.TaskGroup (GH-94359) (GH-94456)
Co-authored-by: CAM Gerlach <CAM.Gerlach at Gerlach.CAM>
(cherry picked from commit b6ec6d4041a5d937f0b63764a329582af4948a3c)
Co-authored-by: Guido van Rossum <guido at python.org>
files:
M Doc/library/asyncio-task.rst
M Doc/whatsnew/3.11.rst
diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index 7796e47d4674d..9b76548433b6a 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -103,6 +103,29 @@ To actually run a coroutine, asyncio provides three main mechanisms:
world
finished at 17:14:34
+* The :class:`asyncio.TaskGroup` class provides a more modern
+ alternative to :func:`create_task`.
+ Using this API, the last example becomes::
+
+ async def main():
+ async with asyncio.TaskGroup() as tg:
+ task1 = tg.create_task(
+ say_after(1, 'hello'))
+
+ task2 = tg.create_task(
+ say_after(2, 'world'))
+
+ print(f"started at {time.strftime('%X')}")
+
+ # The wait is implicit when the context manager exits.
+
+ print(f"finished at {time.strftime('%X')}")
+
+ The timing and output should be the same as for the previous version.
+
+ .. versionadded:: 3.11
+ :class:`asyncio.TaskGroup`.
+
.. _asyncio-awaitables:
@@ -223,6 +246,11 @@ Creating Tasks
:exc:`RuntimeError` is raised if there is no running loop in
current thread.
+ .. note::
+
+ :meth:`asyncio.TaskGroup.create_task` is a newer alternative
+ that allows for convenient waiting for a group of related tasks.
+
.. important::
Save a reference to the result of this function, to avoid
@@ -254,6 +282,76 @@ Creating Tasks
Added the *context* parameter.
+Task Groups
+===========
+
+Task groups combine a task creation API with a convenient
+and reliable way to wait for all tasks in the group to finish.
+
+.. class:: TaskGroup()
+
+ An :ref:`asynchronous context manager <async-context-managers>`
+ holding a group of tasks.
+ Tasks can be added to the group using :meth:`create_task`.
+ All tasks are awaited when the context manager exits.
+
+ .. versionadded:: 3.11
+
+ .. method:: create_task(coro, *, name=None, context=None)
+
+ Create a task in this task group.
+ The signature matches that of :func:`asyncio.create_task`.
+
+Example::
+
+ async def main():
+ async with asyncio.TaskGroup() as tg:
+ task1 = tg.create_task(some_coro(...))
+ task2 = tg.create_task(another_coro(...))
+ print("Both tasks have completed now.")
+
+The ``async with`` statement will wait for all tasks in the group to finish.
+While waiting, new tasks may still be added to the group
+(for example, by passing ``tg`` into one of the coroutines
+and calling ``tg.create_task()`` in that coroutine).
+Once the last task has finished and the ``async with`` block is exited,
+no new tasks may be added to the group.
+
+The first time any of the tasks belonging to the group fails
+with an exception other than :exc:`asyncio.CancelledError`,
+the remaining tasks in the group are cancelled.
+At this point, if the body of the ``async with`` statement is still active
+(i.e., :meth:`~object.__aexit__` hasn't been called yet),
+the task directly containing the ``async with`` statement is also cancelled.
+The resulting :exc:`asyncio.CancelledError` will interrupt an ``await``,
+but it will not bubble out of the containing ``async with`` statement.
+
+Once all tasks have finished, if any tasks have failed
+with an exception other than :exc:`asyncio.CancelledError`,
+those exceptions are combined in an
+:exc:`ExceptionGroup` or :exc:`BaseExceptionGroup`
+(as appropriate; see their documentation)
+which is then raised.
+
+Two base exceptions are treated specially:
+If any task fails with :exc:`KeyboardInterrupt` or :exc:`SystemExit`,
+the task group still cancels the remaining tasks and waits for them,
+but then the initial :exc:`KeyboardInterrupt` or :exc:`SystemExit`
+is re-raised instead of :exc:`ExceptionGroup` or :exc:`BaseExceptionGroup`.
+
+If the body of the ``async with`` statement exits with an exception
+(so :meth:`~object.__aexit__` is called with an exception set),
+this is treated the same as if one of the tasks failed:
+the remaining tasks are cancelled and then waited for,
+and non-cancellation exceptions are grouped into an
+exception group and raised.
+The exception passed into :meth:`~object.__aexit__`,
+unless it is :exc:`asyncio.CancelledError`,
+is also included in the exception group.
+The same special case is made for
+:exc:`KeyboardInterrupt` and :exc:`SystemExit` as in the previous paragraph.
+
+
Sleeping
========
@@ -327,8 +425,9 @@ Running Tasks Concurrently
cancellation of one submitted Task/Future to cause other
Tasks/Futures to be cancelled.
- .. versionchanged:: 3.10
- Removed the *loop* parameter.
+ .. note::
+ A more modern way to create and run tasks concurrently and
+ wait for their completion is :class:`asyncio.TaskGroup`.
.. _asyncio_example_gather:
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index 0275b44d38db1..0aa44295f73bb 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -443,6 +443,11 @@ asyncio
the asyncio library. (Contributed by Yves Duprat and Andrew Svetlov in
:gh:`87518`.)
+* Add :class:`~asyncio.TaskGroup` class,
+ an :ref:`asynchronous context manager <async-context-managers>`
+ holding a group of tasks that will wait for all of them upon exit.
+ (Contributed by Yury Seliganov and others.)
+
datetime
--------
More information about the Python-checkins
mailing list