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 with the aim to simplify async code usage for common wide-spread scenarios.

Executando um programa asyncio

asyncio.run(coro, *, debug=None)

Executa a corrotina coro e retorna o resultado.

Esta função executa a corrotina passada, tomando cuidado de gerenciar o laço de eventos asyncio, finalizando geradores assíncronos, e encerrando a threadpool.

Esta função não pode ser chamada quando outro laço de eventos asyncio está executando na mesma 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 Modo de Depuração settings.

Esta função sempre cria um novo laço de eventos e fecha-o no final. Ela deve ser usada como um ponto de entrada principal para programas asyncio, e deve idealmente ser chamada apenas uma vez.

Exemplo:

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

asyncio.run(main())

Novo na versão 3.7.

Alterado na versão 3.9: Atualizado para usar loop.shutdown_default_executor().

Alterado na versão 3.10: debug is None by default to respect the global debug mode settings.

Runner context manager

class asyncio.Runner(*, debug=None, loop_factory=None)

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 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 Modo de Depuração 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.

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())

Novo na versão 3.11.

run(coro, *, context=None)

Run a coroutine coro in the embedded loop.

Return the coroutine’s result or raise its exception.

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

Esta função não pode ser chamada quando outro laço de eventos asyncio está executando na mesma thread.

close()

Close the runner.

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

get_loop()

Return the event loop associated with the runner instance.

Nota

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().

Handling Keyboard Interruption

Novo na versão 3.11.

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 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.