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]

Reply via email to