Task Group
- current_taskgroup
A
contextvars.ContextVar
that has the reference to the current innermostTaskGroup
instance. Available only in Python 3.7 or later.
- current_ptaskgroup
A
contextvars.ContextVar
that has the reference to the current innermostPersistentTaskGroup
instance. Available only in Python 3.7 or later.Warning
This is set only when
PersistentTaskGroup
is used with theasync with
statement.
- class TaskGroup(*, name=None)
Provides a guard against a group of tasks spawend via its
create_task()
method instead of the vanilla fire-and-forgettingasyncio.create_task()
.See the motivation and rationale in the trio’s documentation.
In Python 3.11 or later, this wraps
asyncio.TaskGroup
with a small extension to set the current taskgroup in a context variable.- create_task(coro, *, name=None)
Spawns a new task inside the taskgroup and returns the reference to the task. Setting the name of tasks is supported in Python 3.8 or later only and ignored in older versions.
- get_name()
Returns the name set when creating the instance.
New in version 1.0.0.
Changed in version 1.5.0: Fixed edge-case bugs by referring the Python 3.11 stdlib’s
asyncio.TaskGroup
implementation, including abrupt cancellation before all nested spawned tasks start without context switches and propagation of the source exception when the context manager (parent task) is getting cancelled but continued. All existing codes should run without any issues, but it is recommended to test thoroughly.
- class PersistentTaskGroup(*, name=None, exception_handler=None)
Provides an abstraction of long-running task groups for server applications. The main use case is to implement a dispatcher of async event handlers, to group RPC/API request handlers, etc. with safe and graceful shutdown. Here “long-running” means that all tasks should keep going even when sibling tasks fail with unhandled errors and such errors must be reported immediately. Here “safety” means that all spawned tasks should be reclaimed before exit or shutdown.
When used as an async context manager, it works similarly to
asyncio.gather()
withreturn_exceptions=True
option. It exits the context scope when all tasks finish, just likeasyncio.TaskGroup
, but it does NOT abort when there are unhandled exceptions from child tasks; just keeps sibling tasks running and reporting errors as they occur (see below).When not used as an async context maanger (e.g., used as attributes of long-lived objects), it persists running until
shutdown()
is called explicitly. Note that it is the user’s responsibility to callshutdown()
becausePersistentTaskGroup
does not provide the__del__()
method.Regardless how it is executed, it lets all spawned tasks run to their completion and calls the exception handler to report any unhandled exceptions immediately. If there are exceptions occurred again in the exception handlers, then it uses
loop.call_exception_handler()
as the last resort.exception_handler should be an asynchronous function that accepts the exception type, exception object, and the traceback, just like
__aexit__()
dunder method. The default handler just prints out the exception log usingtraceback.print_exc()
. Note that the handler is invoked within the exception handling context and thussys.exc_info()
is also available.Since the exception handling and reporting takes places immediately, it eliminates potential arbitrary report delay due to other tasks or the execution method. This resolves a critical debugging pain when only termination of the application displays accumulated errors, as sometimes we don’t want to terminate but just inspect what is happening.
- create_task(coro, *, name=None)
Spawns a new task inside the taskgroup and returns the reference to a
future
describing the task result. Setting the name of tasks is supported in Python 3.8 or later only and ignored in older versions.You may
await
the retuned future to take the task’s return value or get notified with the exception from it, while the exception handler is still invoked. Since it is just a secondary future, you cannot cancel the task explicitly using it. To cancel the task(s), useshutdown()
or exit the task group context.Warning
In Python 3.6,
await
-ing the returned future hangs indefinitely. We do not fix this issue because Python 3.6 is now EoL (end-of-life) as of December 2021.
- get_name()
Returns the name set when creating the instance.
- async shutdown()
Triggers immediate shutdown of this taskgroup, cancelling all unfinished tasks and waiting for their completion.
- classmethod all_ptaskgroups()
Returns a sequence of all currently existing non-exited persistent task groups.
New in version 1.5.0.
New in version 1.4.0.
Changed in version 1.5.0: Rewrote the overall implementation referring the Python 3.11 stdlib’s
asyncio.TaskGroup
implementation and adapting it to the semantics for “persistency”. All existing codes should run without any issues, but it is recommended to test thoroughly.Changed in version 1.6.1: It no longer raises
BaseExceptionGroup
orExceptionGroup
upon exit orshutdown()
, because it no longer stores the history of unhnadled exceptions from subtasks to prevent memory leaks for long-running persistent task groups. The users must register explicit exception handlers or task done callbacks to report or process such unhandled exceptions.
- exception TaskGroupError
Represents a collection of errors raised inside a task group. Callers may iterate over the errors using the
__errors__
attribute.In Python 3.11 or later, this is a mere wrapper of underlying
BaseExceptionGroup
. This allows existing user codes to run without modification while users can take advantage of the newexcept*
syntax andExceptionGroup
methods if they use Python 3.11 or later. Note that if none of the passed exceptions passed is aBaseException
, it automatically becomesExceptionGroup
.