gh-116048: restructure task group docs and add docs for eager execution (#156102)

kumaraditya303 <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/862befdb99c8268f570c28adaa342eb6586fa0dc
commit: 862befdb99c8268f570c28adaa342eb6586fa0dc
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-08-21T22:31:01+05:30
summary:

gh-116048: restructure task group docs and add docs for eager execution (#156102)

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

diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst
index c6f0fb8a50917a..689a6a6d2e0bd9 100644
--- a/Doc/library/asyncio-task.rst
+++ b/Doc/library/asyncio-task.rst
@@ -402,69 +402,85 @@ Example::
             task2 = tg.create_task(another_coro(...))
         print(f"Both tasks have completed now: {task1.result()}, {task2.result()}")
 
-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).  There is also opportunity to
-request termination of the entire task group with ``tg.cancel()``, based on some condition.
-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.
-No further tasks can then be added to the group.
-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.
-There is an additional special case made only for the body of the
-``async with``: if it raises :exc:`GeneratorExit` and none of the
-other tasks raise exceptions that would be reported, then the
-:exc:`GeneratorExit` is reraised.
-
-Task groups are careful not to mix up the internal cancellation used to
-"wake up" their :meth:`~object.__aexit__` with cancellation requests
-for the task in which they are running made by other parties.
+A few points to keep in mind when using task groups:
+
+* 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.
+
+* Termination of the entire task group may be requested with
+  ``tg.cancel()``, based on some condition.
+
+* If the group is shut down (e.g. because another task failed) before
+  a newly created task has started running, the task is cancelled
+  without its coroutine executing at all, not even to its first
+  ``await``.  To guarantee that the coroutine starts, create the task
+  eagerly with ``eager_start=True`` or use
+  :func:`asyncio.eager_task_factory`.  For example::
+
+      async def job():
+          print("job started")  # never printed
+          try:
+              await asyncio.sleep(1)
+          finally:
+              print("job cleaned up")  # never printed
+
+      async def main():
+          async with asyncio.TaskGroup() as tg:
+              tg.create_task(job())
+              raise RuntimeError  # shuts down the group before job() runs
+
+  With ``tg.create_task(job(), eager_start=True)``, ``job()`` runs up
+  to the ``await``, is cancelled there, and both messages are printed.
+
+When any of the tasks belonging to the group fails with an exception
+other than :exc:`asyncio.CancelledError` (or the body of the
+``async with`` statement exits with an exception, which is treated
+the same way):
+
+* The first time this happens, the remaining tasks in the group are
+  cancelled and then waited for, and no further tasks can be added to
+  the group.  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, the non-cancellation exceptions --
+  including the exception the body exited with, unless it is
+  :exc:`asyncio.CancelledError` -- are combined in an
+  :exc:`ExceptionGroup` or :exc:`BaseExceptionGroup`
+  (as appropriate; see their documentation), which is then raised.
+
+* Some 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`.
+  Additionally, if the body of the ``async with`` statement raises
+  :exc:`GeneratorExit` and none of the other tasks raise exceptions
+  that would be reported, the :exc:`GeneratorExit` is re-raised.
+
+Task groups are careful not to mix up the internal cancellation used
+to "wake up" their :meth:`~object.__aexit__` with cancellation
+requests for the task in which they are running made by other parties.
 In particular, when one task group is syntactically nested in another,
-and both experience an exception in one of their child tasks simultaneously,
-the inner task group will process its exceptions, and then the outer task group
-will receive another cancellation and process its own exceptions.
+and both experience an exception in one of their child tasks
+simultaneously, the inner task group will process its exceptions, and
+then the outer task group will receive another cancellation and
+process its own exceptions.
 
 In the case where a task group is cancelled externally and also must
 raise an :exc:`ExceptionGroup`, it will call the parent task's
-:meth:`~asyncio.Task.cancel` method. This ensures that a
+:meth:`~asyncio.Task.cancel` method.  This ensures that a
 :exc:`asyncio.CancelledError` will be raised at the next
-:keyword:`await`, so the cancellation is not lost.
-
-Task groups preserve the cancellation count
-reported by :meth:`asyncio.Task.cancelling`.
+:keyword:`await`, so the cancellation is not lost.  Task groups also
+preserve the cancellation count reported by
+:meth:`asyncio.Task.cancelling`.
 
 .. versionchanged:: 3.13
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.