Task Scope
- class ShieldScope(timeout: float | None = None)[source]
A context-manager to make the codes within the scope to be shielded from cancellation, delaying any cancellation attempts in the middle to be re-raised afterwards. You may use it as an async context manager as well.
See https://github.com/python/cpython/issues/99714#issuecomment-1817941789 for the original ideation.
To self-cancel from the inside of ShieldScope, you may simply raise
asyncio.CancelledError.async def work(): try: await some_job() finally: with ShieldScope(): await cleanup() # ensured to run regardless of cancellation timing task = asyncio.create_task(work()) ... await cancel_and_wait(task)
It may be used as either an async context manager or a context manager. As an async context manager, it is equivalent to
TaskScope(shield=True). As a (sync) coontext manager, you cannot spawn child tasks because it does not wait for children’s completion when exiting the context scope. You may use it as a simple block marker to wrap the code lines to be shielded.When there are multiple nested ShieldScope combined with TaskScope, the cancellation is deferred to the point of exit of the topmost
ShieldScope(orTaskScop(shield=True)) block where the parent scope is not shielded or there is no parent scope.Added in version 2.1.
Changed in version 2.2: Moved from
aiotools.cancelmodule toaiotools.taskscopemodule to subclassTaskScopewithout circular imports. The root import pathaiotools.ShieldScopeis preserved.Changed in version 2.2: When used as a synchronous context manager, spawning child tasks is explicitly prohibited.
- class TaskScope(*, shield: bool = False, timeout: float | None = None, exception_handler: Callable[[ErrorArg], None] | LoopExceptionHandler | None = LoopExceptionHandler.TOKEN, context: Context | None = None)[source]
TaskScope is an asynchronous context manager which implements structured concurrency, i.e., “scoped” cancellation, over a set of child tasks. It terminates when all child tasks make conclusion (either results or exceptions).
TaskScopesubclassesTaskContext, but it mandates use ofasync withblocks to clarify which task is the parent of the child tasks spawned viacreate_task().The key difference to
asyncio.TaskGroupis that it allows customization of the exception handling logic for unhandled child task exceptions, instead cancelling all pending child tasks upon any unhandled child task exceptions and collecting them as anExceptionGroup.Since
TaskScopemay be used for a long-running server context, unhandled child exceptions are NOT stored at all, but passed to the exception handler directly and immediately. If you want to collect results and exceptions, please useas_completed_safe()orgather_safe().When
shield=True, the scope is protected from outside cancellations, while re-raising the requested cancellations when terminated. From inside, you may simply raise a newasyncio.CancelledErrorfrom the context manager body to self-cancel the shielded scope.You may nest and mix multiple TaskScope and even
asyncio.TaskGroupwithin a single task or via subtask chains. In such cases, cancelling the outmost parent task will be shielded by the topmost TaskScope withshield=TrueorShieldScope.The
timeoutargument enforces timeout even when the scope is shielded, unlike the vanillaasyncio.timeout().Refer
TaskContextfor the descriptions about the constructor arguments.Based on this customizability,
Supervisoris a mere alias ofTaskScopewithexception_handler=None.Added in version 2.0.
Changed in version 2.1: In Python 3.14 or higher, it also updates the asyncio call graph so that the task awaiter could be tracked down via TaskScope, like
asyncio.TaskGroup.Changed in version 2.1: Added the
shieldandtimeoutoptions.Changed in version 2.2: Now nesting and mixing TaskScope and TaskGroup is fully supported and behaves consistently, by deferring the cancellation at the topmost shielded scope.
- abort(msg: str | None = None) None[source]
Triggers cancellation of the scope and all its children and immediately returns without waiting for completion. This method ignores the shield option.
- async aclose() None[source]
Triggers cancellation of the scope and all its children, then waits for completion. This method ignores the shield option.
Calling this method will cancel the host task and the task body will observe a
asyncio.CancelledError.
- move_on_after(timeout: float | None = None, shield: bool = False) AsyncIterator[TaskScope][source]
A shortcut to create a
TaskScopewith a timeout while ignoring and continuing after timeout.prior_work() async with move_on_after(3.0) as ts: ts.create_task(...) await some_work() # after 3.0 seconds, any remaining coroutines/tasks within the scope is cancelled, # and the control resumes here. after_work()
Added in version 2.1.