# asyncio-runner

<div class="currentmodule">

asyncio

</div>

# Runners

**Source code:** `Lib/asyncio/runners.py`

This section outlines high-level asyncio primitives to run asyncio code.

They are built on top of an `event loop <asyncio-event-loop>` with the aim to simplify async code usage for common wide-spread scenarios.

<div class="contents" data-depth="1" data-local="">

</div>

## Running an asyncio Program

<div class="function">

run(coro, \*, debug=None, loop_factory=None, eager_tasks=False)

Execute *coro* in an asyncio event loop and return the result.

The argument can be any awaitable object.

This function runs the awaitable, taking care of managing the asyncio event loop, *finalizing asynchronous generators*, and closing the executor.

This function cannot be called when another asyncio event loop is running in the same thread.

If *debug* is `True`, the event loop will be run in debug mode. `False` disables debug mode explicitly. `None` is used to respect the global `asyncio-debug-mode` settings.

If *loop_factory* is not `None`, it is used to create a new event loop; otherwise `asyncio.new_event_loop` is used. The loop is closed at the end. This function should be used as a main entry point for asyncio programs, and should ideally only be called once. It is recommended to use *loop_factory* to configure the event loop.

If *eager_tasks* is `True`, the created loop is configured to use `eager-task-factory` by default.

The executor is given a timeout duration of 5 minutes to shutdown. If the executor hasn't finished within that duration, a warning is emitted and the executor is closed.

> [!NOTE]
> Users concerned about performance are encouraged to experiment with `eager_tasks=True` in their code.

Example:

    async def main():
        await asyncio.sleep(1)
        print('hello')

    asyncio.run(main())

<div class="versionadded">

3.7

</div>

<div class="versionchanged">

3.9 Updated to use `loop.shutdown_default_executor`.

</div>

<div class="versionchanged">

3.10

*debug* is `None` by default to respect the global debug mode settings.

</div>

<div class="versionchanged">

3.12

Added *loop_factory* parameter.

</div>

<div class="versionchanged">

3.14

*coro* can be any awaitable object.

</div>

<div class="versionchanged">

3.16

Added *eager_tasks* parameter.

</div>

</div>

## Runner context manager

<div class="Runner(*, debug=None, loop_factory=None, eager_tasks=False)">

A context manager that simplifies *multiple* async function calls in the same context.

Sometimes several top-level async functions should be called in the same `event
loop <asyncio-event-loop>` and `contextvars.Context`.

If *debug* is `True`, the event loop will be run in debug mode. `False` disables debug mode explicitly. `None` is used to respect the global `asyncio-debug-mode` settings.

*loop_factory* could be used for overriding the loop creation. It is the responsibility of the *loop_factory* to set the created loop as the current one. By default `asyncio.new_event_loop` is used and set as current event loop with `asyncio.set_event_loop` if *loop_factory* is `None`.

If *eager_tasks* is `True`, the created loop is configured to use `eager-task-factory` by default.

Basically, `asyncio.run` example can be rewritten with the runner usage:

    async def main():
        await asyncio.sleep(1)
        print('hello')

    with asyncio.Runner() as runner:
        runner.run(main())

> [!NOTE]
> Users concerned about performance are encouraged to experiment with `eager_tasks=True` in their code.

<div class="versionadded">

3.11

</div>

<div class="versionchanged">

3.16

Added *eager_tasks* parameter.

</div>

<div class="method">

run(coro, \*, context=None)

Execute *coro* in the embedded event loop.

The argument can be any awaitable object.

If the argument is a coroutine, it is wrapped in a Task.

An optional keyword-only *context* argument allows specifying a custom `contextvars.Context` for the code to run in. The runner's default context is used if context is `None`.

Returns the awaitable's result or raises an exception.

This function cannot be called when another asyncio event loop is running in the same thread.

<div class="versionchanged">

3.14

*coro* can be any awaitable object.

</div>

</div>

<div class="method">

close()

Close the runner.

Finalize asynchronous generators, shutdown default executor, close the event loop and release embedded `contextvars.Context`.

</div>

<div class="method">

get_loop()

Return the event loop associated with the runner instance.

</div>

> [!NOTE]
> `Runner` uses the lazy initialization strategy, its constructor doesn't initialize underlying low-level structures.
>
> Embedded *loop* and *context* are created at the `with` body entering or the first call of `run` or `get_loop`.

</div>

## Handling Keyboard Interruption

<div class="versionadded">

3.11

</div>

When `signal.SIGINT` is raised by `Ctrl-C`, `KeyboardInterrupt` exception is raised in the main thread by default. However this doesn't work with `asyncio` because it can interrupt asyncio internals and can hang the program from exiting.

To mitigate this issue, `asyncio` handles `signal.SIGINT` as follows:

1.  `asyncio.Runner.run` installs a custom `signal.SIGINT` handler before any user code is executed and removes it when exiting from the function.
2.  The `~asyncio.Runner` creates the main task for the passed coroutine for its execution.
3.  When `signal.SIGINT` is raised by `Ctrl-C`, the custom signal handler cancels the main task by calling `asyncio.Task.cancel` which raises `asyncio.CancelledError` inside the main task. This causes the Python stack to unwind, `try/except` and `try/finally` blocks can be used for resource cleanup. After the main task is cancelled, `asyncio.Runner.run` raises `KeyboardInterrupt`.
4.  A user could write a tight loop which cannot be interrupted by `asyncio.Task.cancel`, in which case the second following `Ctrl-C` immediately raises the `KeyboardInterrupt` without cancelling the main task.