-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
gh-101581: Add asyncio.TaskScope and let asyncio.TaskGroup subclass it
#105011
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
The rewritten TaskGroup still passes all existing test cases.
- Also fixes `__all__` import of the taskgroups and taskscope modules
- asyncio's module names are plural.
|
I'm work in progress to revamp the test cases copied from #101648. |
|
Under experimentation in achimnol/aiotools#58. |
|
@achimnol, in general I like the solution. However, I'm wondering if instead of using inheritance we can somehow use composition for this. |
|
I'm going to restart the experimentation including combination with |
|
I've done context-manager-style shielding via https://aiotools.readthedocs.io/en/latest/aiotools.taskscope.html#aiotools.taskscope.ShieldScope The aiotools implementation is mostly independent to asyncio internals, but it requires overriding I'm currently thinking about how this could be implemented into the stdlib's |
|
@1st1 @asvetlov @kumaraditya303 @gvanrossum I have several concerns before continuing the work in this PR:
|
|
Can we take a step back and go back to what this PR wants to achieve? A summary would be great for everyone seeing this for the first time. |
(revised at 2025-10-15; before revision) This is what I understand now. |
|
To split out cancellation scopes and task-managing scopes to make them reusable and composable, we need ability to customize Using a task factory could be one solution, but it has a crucial limitation: what if there are multiple libraries and user codes who want to install their own task factories? Since asyncio is a stdlib, I'm afraid how far extent we could introduce some semantic (=breaking) changes to reconcile anyio and aiotools implementations. |

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 likeTaskGroupwhich 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.Supervisoris same toTaskScope(delegate_errors=None), soTaskScopeis 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): Callsfuncusing the samecontextargument likeloop.call_exception_handler()TaskGroupis rewritten by subclassingTaskScope, and now its code highlights the specific semantics of theTaskGroupAPI. The rewrittenTaskGroupstill passes all existing test cases.To prevent memory leaks in long-lived use cases,
TaskScopejust keeps aself._has_errorsboolean flag only while TaskGroup keeps the full listself._errors.asyncio.TaskGroup#101581