codestare.async_utils.nursery module

class codestare.async_utils.nursery.ExcInfo(exc_type: Optional[Type[Exception]] = None, exc: Optional[Exception] = None, traceback: Optional[Any] = None)

Bases: NamedTuple

As returned by sys.exc_info()

exc_type: Optional[Type[Exception]]

Alias for field number 0

exc: Optional[Exception]

Alias for field number 1

traceback: Optional[Any]

Alias for field number 2

codestare.async_utils.nursery.mark_sentinel_task(task: Task) Task

The package keeps a global reference to all tasks marked as a sentinel task i.e. they will not be garbage collected even if they are finished. Those tasks will be stopped before other tasks if you set up the shutdown handling with this module. These tasks are used by the TaskNursery to trigger the nursery’s shutdown by simply canceling / stopping the sentinel_task.

When a shutdown() task is created (e.g. when a specific signal is received), all sentinel tasks will be stopped first – this will trigger the shutdown of all TaskNurserys that were created in the running interpreter.

Parameters:

task – will be globally referenced to be shut down eventually

Returns:

the task

codestare.async_utils.nursery.handle_exception(loop: AbstractEventLoop, context) None

Calls the loops default exception handler, then creates a shutdown() task for the loop. This is the default exception handler that is set up for a loop with setup_shutdown_handling() if no other specific exception handler is set.

Parameters:
  • loop – event loop to shut down

  • context – meta information for shutdown() task

async codestare.async_utils.nursery.stop_task(task: Task) Any

Try to cancel the task, and await it – returns possible exceptions raised inside the task, e.g. the asyncio.CancelledError raised by canceling the task prematurely.

Parameters:

task – task to stop, can’t be the current running task

Returns:

result of task

Raises:

ValueError – if the currently running task is passed

codestare.async_utils.nursery.setup_shutdown_handling(loop: AbstractEventLoop) None

Add signal handling and exception handling for the loop. On Windows only signal.SIGBREAK and signal.SIGINT are usable, on Linux add handling for signal.SIGHUP, signal.SIGTERM and signal.SIGINT.

Uses normal signal handlers using signal except of asyncio.loop.add_signal_handler() since the latter is not available on Windows systems.

The signal handlers registered start a shutdown() task for the passed loop. Also sets handle_exception() as the loop’s exception handler if no other handler is already set.

Parameters:

loop – event loop

async codestare.async_utils.nursery.shutdown(loop: AbstractEventLoop, **kwargs) None

Await shutdown of the loop.

If the shutdown() task is triggered by an exception from a task managed by a TaskNursery, the **kwargs will contain a reference to the nursery’s sentinel_task in the context argument. If this is the case, this sentinel task will be stopped (using stop_task()) which triggers trigger the shutdown handling of the TaskNursery.

If the shutdown() task is triggered by a signal handler, all tasks marked as sentinel tasks will be stopped first, instead.

Finally all tasks still running in the loop will be stopped. Then the loop will be stopped.

Parameters:
  • loop – event loop that needs to be stopped

  • **kwargs – meta information like received signal or exception context

class codestare.async_utils.nursery.TaskNursery(name: str | Callable[[], str] | None = None, loop: asyncio.BaseEventLoop | None = None)

Bases: AsyncExitStack, Registry

Use a TaskNursery to create and manage tasks, instead of using the asyncio.loop.create_task() method directly.

All tasks created by a nursery receive a callback to execute when they finish, which will trigger a graceful shutdown of the event loop if necessary – this saves a lot of boilerplate code

Basically, a TaskNursery is a contextlib.AsyncExitStack that gets closed when it’s sentinel_task is cancelled. This means you can enter any number of async context managers with a nursery, and every one of them will be closed when the nursery shuts down.

Creating a TaskNursery for an event loop also sets up signal handling and exception handling for that loop, i.e. if a loop has an active TaskNursery exiting the interpreter forcefully (e.g. Ctrl+C in the shell) will gracefully shut down all nurseries first.

TaskNursery is a Registry with unique attribute name by default, i.e. all instances need to have a unique name and can be referenced by name using the TaskNursery.registry

>>> from codestare.async_utils import TaskNursery
>>> import asyncio
>>> loop = asyncio.get_event_loop_policy().get_event_loop()
>>> nursery = TaskNursery(name="Foo", loop=loop)
>>> TaskNursery.registry
{'Foo': codestare.async_utils.nursery.TaskNursery(name='Foo', loop=<...>)}
registry

Inherited from codestare.async_utils.helper.Registry

static add_shutdown_handling(loop: AbstractEventLoop) None

Add shutdown handling to the loop, once. Caches loops that were passed as argument to this method, additional calls with the same loop will be ignored.

Parameters:

loop – gets shutdown handling applied with setup_shutdown_handling()

stop_task(task: Task)

Try to stop a task managed by this nursery using stop_task()

Parameters:

task – should be stopped

Returns:

result of stop_task()

Raises:

ValueError – if the task is not managed by this nursery i.e. it was not created by create_task()

__init__(name: str | Callable[[], str] | None = None, loop: asyncio.BaseEventLoop | None = None)

Creates a nursery on the loop – or the current running loop.

Parameters:
  • name – if a callable is used, the name can be dynamic

  • loop – if no loop is passed the running loop is used

loop: BaseEventLoop

Event loop instance to start tasks in, either passed as loop or current running loop

sentinel_task: Task

Dummy task that does nothing but cancelling it triggers the closing of this TaskNursery i.e. all entered async contexts will be closed and all created tasks will be stopped.

property name

Unique name of nursery, used as Registry key by default, see __unique_key_attr__. If a callable is passed as name during initialization, this callable is used to compute the name dynamically.

property tasks

Reference to managed tasks

pop_all() TaskNursery

Preserve the context stack by transferring it to a new instance.

create_task(coro: Generator[_TaskYieldType, None, T] | Awaitable[T], **kwargs) asyncio.Task[T]

Tasks created with this method have a callback which runs when the task finishes and tries to retrieve the task result. If the task raised an error the loops exception handler is called with the exception info and a reference to this nursery’s sentinel_task. Typically, the exception handler is handle_exception() (it is set as exception handler when the TaskNursery is created for a loop without specific exception handler). It will cancel the sentinel task and trigger the nursery’s shutdown.

Parameters:
Returns:

reference to created task