gh-101581: Add asyncio.TaskScope
and let asyncio.TaskGroup
subclass it
#105011
+864
−184
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This is yet another approach to embrace long-lived use cases of TaskGroup, as an alternative to #101648.
It adds a new lower-level task lifecycle management primitive,
TaskScope
. It provides a base scope for cancellation and tracking of child tasks, and can be extended to add specific semantics likeTaskGroup
which cancels all children altogether upon any first seen unhandled exception. aiotools demonstrates other new coroutine aggregation interfaces likerace()
,as_completed_safe()
, andgather_safe()
based onTaskScope
. (Note: the currentaiotools.Supervisor
is same toTaskScope(delegate_errors=None)
, soTaskScope
is a generalization ofSupervisor
.)The implementation combines the 1st and the 2nd ideas as discussed about expressing how to handle the exceptions:
delegate_errors
.TaskScope()
: Callsloop.call_exception_handler()
upon unhandled exceptionsTaskScope(delegate_errors=None)
: Silently ignores unhandled exceptionsTaskGroup
)TaskScope(delegate_errors=func)
: Callsfunc
using the samecontext
argument likeloop.call_exception_handler()
TaskGroup
is rewritten by subclassingTaskScope
, and now its code highlights the specific semantics of theTaskGroup
API. The rewrittenTaskGroup
still passes all existing test cases.To prevent memory leaks in long-lived use cases,
TaskScope
just keeps aself._has_errors
boolean flag only while TaskGroup keeps the full listself._errors
.asyncio.TaskGroup
#101581