Exécuteurs (*runners*)
**********************

**Code source :** Lib/asyncio/runners.py

Cette section décrit les primitives *asyncio* de haut niveau pour
exécuter du code asynchrone.

Elles sont construites au-dessus d'une boucle d'événements dans le but
de simplifier l'utilisation du code asynchrone pour les scénarios les
plus courants.

* Exécution d'un programme asynchrone

* Gestionnaire de contexte de l'exécuteur

* Gestion de l'interruption par le clavier


Exécution d'un programme asynchrone
===================================

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

   Exécute la *coroutine* *coro* et renvoie le résultat.

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

   Cette fonction ne peut pas être appelée lorsqu'une autre boucle
   d'événement asynchrone est en cours d'exécution dans le même fil
   d'exécution.

   Si *debug* vaut "True", la boucle d'événements est exécutée en mode
   débogage. "False" désactive explicitement le mode débogage. "None"
   est utilisée pour respecter les paramètres globaux définis par Mode
   débogage.

   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.

   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.

   Exemple :

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

      asyncio.run(main())

   Ajouté dans la version 3.7.

   Modifié dans la version 3.9: mise à jour pour utiliser
   "loop.shutdown_default_executor()".

   Modifié dans la version 3.10: *debug* vaut "None" par défaut pour
   respecter les paramètres du mode de débogage global.

   Modifié dans la version 3.12: Added *loop_factory* parameter.


Gestionnaire de contexte de l'exécuteur
=======================================

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

   Gestionnaire de contexte englobant *plusieurs* appels de fonctions
   asynchrones dans le même contexte.

   Parfois, plusieurs fonctions asynchrones de niveau supérieur
   doivent être appelées dans la même boucle d'événements et le même
   "contextvars.Context".

   Si *debug* vaut "True", la boucle d'événements est exécutée en mode
   débogage. "False" désactive explicitement le mode débogage. "None"
   est utilisée pour respecter les paramètres globaux définis par Mode
   débogage.

   *loop_factory* peut être utilisée pour remplacer la création de la
   boucle. *loop_factory* a la responsabilité de définir la boucle
   créée comme boucle courante. Par défaut "asyncio.new_event_loop()"
   est utilisée et définie comme boucle d'événements actuelle avec
   "asyncio.set_event_loop()" si *loop_factory* vaut "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())

   Ajouté dans la version 3.11.

   run(coro, *, context=None)

      Exécute la *coroutine* *coro* dans la boucle d'événements en
      cours.

      Renvoie le résultat de la coroutine ou lève les exceptions
      afférentes.

      L'argument (uniquement nommé) facultatif *context* permet de
      spécifier un "contextvars.Context" personnalisé pour la
      coroutine à exécuter. Le contexte par défaut de l'exécuteur est
      utilisé si *context* est "None".

      Cette fonction ne peut pas être appelée lorsqu'une autre boucle
      d'événement asynchrone est en cours d'exécution dans le même fil
      d'exécution.

   close()

      Termine l'exécuteur.

      Finalise les générateurs asynchrones, arrête l'exécuteur par
      défaut, ferme la boucle d'événements et libère le
      "contextvars.Context" en cours.

   get_loop()

      Renvoie la boucle d'événements associée à l'instance de
      l'exécuteur.

   Note:

     "Runner" utilise la stratégie d'initialisation paresseuse, son
     constructeur n'initialise pas les structures de bas niveau sous-
     jacentes.La  boucle d'événements *loop* et le *context* intégrés
     sont créés à l'entrée du corps de "with" ou au premier appel de
     "run()" ou "get_loop()".


Gestion de l'interruption par le clavier
========================================

Ajouté dans la version 3.11.

Lorsque "signal.SIGINT" est déclenché par "Ctrl"-"C", l'exception
"KeyboardInterrupt" est levée par défaut dans le fils d'exécution
principal. Cependant, cela ne fonctionne pas avec "asyncio" car cela
peut interrompre le fonctionnement interne de *asyncio* et empêcher le
programme de se terminer.

Pour contrer ce problème, "asyncio" gère "signal.SIGINT" comme suit :

1. "asyncio.Runner.run()" installe un gestionnaire "signal.SIGINT"
   personnalisé avant l'exécution de tout code utilisateur et le
   supprime à la sortie de la fonction.

2. Le "Runner" crée la tâche principale pour la coroutine transmise
   pour son exécution.

3. Lorsque "signal.SIGINT" est déclenché par "Ctrl"-"C", le
   gestionnaire de signal personnalisé annule la tâche principale en
   appelant "asyncio.Task.cancel()" qui lève "asyncio.CancelledError"
   à l'intérieur de la tâche principale. Cela entraîne la remontée
   dans la pile Python, les blocs "try/except" et "try/finally"
   peuvent être utilisés pour le nettoyage des ressources. Une fois la
   tâche principale annulée, "asyncio.Runner.run()" lève
   "KeyboardInterrupt".

4. Un utilisateur peut écrire une boucle tellement petite qu'elle ne
   peut pas être interrompue par "asyncio.Task.cancel()" ; dans ce cas
   la seconde suivante "Ctrl"-"C" lève immédiatement le
   "KeyboardInterrupt" sans annuler la tâche principale.
