# Runners

Código fuente: Lib/asyncio/runners.py

Esta sección describe las primitivas asyncio de alto nivel para ejecutar código asyncio.

Están construidos sobre un event loop con el objetivo de simplificar el uso de código async para escenarios comunes de alta difusión.

## Ejecutando un programa asyncio

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

  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.

  Esta función no puede ser llamada cuando otro bucle de eventos asyncio está corriendo en el mismo hilo.

  Si debug es `True`, el bucle de eventos se ejecutará en modo depuración. `False` deshabilita el modo depuración de manera explícita. `None` se usa para respetar la configuración global Modo depuración.

  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 instead of policies. Passing `asyncio.EventLoop` allows running asyncio without the policy system.

  Al ejecutor se le da un tiempo de espera de 5 minutos para apagarse. Si el ejecutor no ha finalizado en ese tiempo, se emite una advertencia y se cierra el ejecutor.

  Ejemplo:

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

      asyncio.run(main())

  Added in version 3.7.

  Distinto en la versión 3.9: Actualizado para usar `loop.shutdown_default_executor()`.

  Distinto en la versión 3.10: debug es `None` por defecto para respetar la configuración global del modo depuración.

  Distinto en la versión 3.12: Añadido el parámetro loop_factory.

  Distinto en la versión 3.14: coro can be any awaitable object.

  Nota

  The `asyncio` policy system is deprecated and will be removed in Python 3.16; from there on, an explicit loop_factory is needed to configure the event loop.

## Gestor de contexto del runner

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

  Un gestor de contexto que simplifica múltiples llamadas a funciones asíncronas en el mismo contexto.

  A veces varias funciones asíncronas de alto nivel deben ser llamadas en el mismo event loop y `contextvars.Context`.

  Si debug es `True`, el bucle de eventos se ejecutará en modo depuración. `False` deshabilita el modo depuración de manera explícita. `None` se usa para respetar la configuración global Modo depuración.

  loop_factory puede ser usado para redefinir la creación de bucles. Es responsabilidad del loop_factory establecer el bucle creado como el actual. Por defecto `asyncio.new_event_loop()` es usado y configura el nuevo bucle de eventos como el actual con `asyncio.set_event_loop()` si loop_factory es `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())

  Added in version 3.11.

  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.

    Esta función no puede ser llamada cuando otro bucle de eventos asyncio está corriendo en el mismo hilo.

    Distinto en la versión 3.14: coro can be any awaitable object.

  close()

    Cierra el runner.

    Termina los generadores asíncronos, apaga el ejecutor por defecto, cierra el bucle de eventos y libera el `contextvars.Context` embebido.

  get_loop()

    Retorna el bucle de eventos asociado a la instancia del runner.

  Nota

  `Runner` usa una estrategia de inicialización perezosa, su constructor no inicializa las estructuras de bajo nivel subyacentes.

  El loop y el context embebidos son creados al entrar al cuerpo `with` o en la primera llamada a `run()` o a `get_loop()`.

## Manejando interrupciones de teclado

Added in version 3.11.

Cuando `signal.SIGINT` es lanzada por `Ctrl`-`C`, la excepción `KeyboardInterrupt` es lanzada en el hilo principal por defecto. Sin embargo, esto no funciona con `asyncio` porque puede interrumpir las funciones internas a asyncio e impedir la salida del programa.

Para mitigar este problema, `asyncio` maneja `signal.SIGINT` de la siguiente forma:

1. `asyncio.Runner.run()` instala un administrador `signal.SIGINT` personalizado antes que cualquier código de usuario sea ejecutado y lo remueve a la salida de la función.

2. La `Runner` crea la tarea principal que será pasada a la co-rutina para su ejecución.

3. Cuando `signal.SIGINT` es lanzada por `Ctrl`-`C`, el administrador de señales personalizado cancela la tarea principal llamando a `asyncio.Task.cancel()` que lanza `asyncio.CancelledError` dentro de la tarea principal. Esto hace que la pila de Python se desenrolle, los bloques `try/except` y `try/finally` se pueden utilizar para la limpieza de recursos. Luego que la tarea principal es cancelada, `asyncio.Runner.run()` lanza `KeyboardInterrupt`.

4. Un usuario podría escribir un bucle cerrado que no puede ser interrumpido por `asyncio.Task.cancel()`, en cuyo caso la segunda llamada a `Ctrl`-`C` lanza inmediatamente `KeyboardInterrupt` sin cancelar la tarea principal.