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

* Gestor de contexto del runner

* Manejando interrupciones de teclado


Ejecutando un programa asyncio
==============================

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

   Ejecutar el *coroutine* *coro* y retornar el resultado.

   Esta función ejecuta la co-rutina pasada, encargándose de gestionar
   el bucle de eventos asyncio, *finalizando los generadores
   asíncronos*, y cerrando el ejecutor.

   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.

   Si *loop_factory* no es "None", se utiliza para crear un nuevo
   bucle de eventos; en caso contrario se utiliza
   "asyncio.new_event_loop()". El bucle se cierra al final. Esta
   función debería usarse como punto de entrada principal para los
   programas asyncio, e idealmente sólo debería llamarse una vez. Se
   recomienda usar *loop_factory* para configurar el bucle de eventos
   en lugar de políticas.

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


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

   Básicamente, el ejemplo "asyncio.run()" puede ser re-escrito usando
   el ejecutor:

      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)

      Ejecuta una *co-rutina* *coro* en el bucle incrustado.

      Retorna el resultado de la co-rutina o lanza excepción de dicha
      co-rutina.

      Un argumento opcional del *context* que consiste en una palabra
      clave permite especificar un "contextvars.Context" personalizado
      donde correr la *coro* . El contexto por defecto del ejecutor es
      usado si el modo debug es "None".

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

   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.
