Stratégies
**********

Une stratégie de boucle d'événements est un objet global, pour chaque
processus, qui contrôle la gestion de la boucle d'événement. Chaque
boucle d'événement a une stratégie par défaut, qui peut être modifiée
et personnalisée à l'aide de l'API de la stratégie.

Une stratégie définit la notion de *contexte* et gère une boucle
d'événement distincte par contexte. La stratégie par défaut définit le
*contexte* comme étant le fil d'exécution actuel.

En utilisant une stratégie de boucle d'événement personnalisée, le
comportement des fonctions "get_event_loop()", "set_event_loop()" et
"new_event_loop()" peut être personnalisé.

Les objets de stratégie doivent implémenter les API définies dans la
classe de base abstraite "AbstractEventLoopPolicy".


Obtenir et définir la stratégie
===============================

Les fonctions suivantes peuvent être utilisées pour obtenir et définir
la stratégie du processus en cours :

asyncio.get_event_loop_policy()

   Renvoie la stratégie actuelle à l'échelle du processus.

asyncio.set_event_loop_policy(policy)

   Définit la stratégie actuelle sur l'ensemble du processus sur
   *policy*.

   Si *policy* est définie sur "None", la stratégie par défaut est
   restaurée.


Sujets de stratégie
===================

La classe de base abstraite de la stratégie de boucle d'événements est
définie comme suit:

class asyncio.AbstractEventLoopPolicy

   Une classe de base abstraite pour les stratégies *asyncio*.

   get_event_loop()

      Récupère la boucle d'évènements pour le contexte actuel.

      Renvoie un objet de boucle d'événements en implémentant
      l'interface "AbstractEventLoop".

      Cette méthode ne devrait jamais renvoyer "None".

      Modifié dans la version 3.6.

   set_event_loop(loop)

      Définit la boucle d'événements du contexte actuel sur *loop*.

   new_event_loop()

      Crée et renvoie un nouvel objet de boucle d'événements.

      Cette méthode ne devrait jamais renvoyer "None".

   get_child_watcher()

      Récupère un objet observateur du processus enfant.

      Renvoie un objet observateur implémentant l'interface
      "AbstractChildWatcher".

      Cette fonction est spécifique à Unix.

   set_child_watcher(watcher)

      Définit l'observateur du processus enfant actuel à *watcher*.

      Cette fonction est spécifique à Unix.

*asyncio* est livré avec les stratégies intégrées suivantes :

class asyncio.DefaultEventLoopPolicy

   La stratégie *asyncio* par défaut. Utilise "SelectorEventLoop" sur
   les plates-formes Unix et "ProactorEventLoop" sur Windows.

   Il n'est pas nécessaire d'installer la stratégie par défaut
   manuellement. *asyncio* est configuré pour utiliser automatiquement
   la stratégie par défaut.

   Modifié dans la version 3.8: On Windows, "ProactorEventLoop" is now
   used by default.

class asyncio.WindowsSelectorEventLoopPolicy

   Stratégie de boucle d'événements alternative utilisant
   l'implémentation de la boucle d'événements "ProactorEventLoop".

   Disponibilité : Windows.

class asyncio.WindowsProactorEventLoopPolicy

   An alternative event loop policy that uses the "ProactorEventLoop"
   event loop implementation.

   Disponibilité : Windows.


Observateurs de processus
=========================

Un observateur de processus permet de personnaliser la manière dont
une boucle d'événements surveille les processus enfants sous Unix.
Plus précisément, la boucle d'événements a besoin de savoir quand un
processus enfant s'est terminé.

Dans *asyncio*, les processus enfants sont créés avec les fonctions
"create_subprocess_exec()" et "loop.subprocess_exec()".

asyncio defines the "AbstractChildWatcher" abstract base class, which
child watchers should implement, and has four different
implementations: "ThreadedChildWatcher" (configured to be used by
default), "MultiLoopChildWatcher", "SafeChildWatcher", and
"FastChildWatcher".

Voir aussi la section sous-processus et fils d'exécution.

Les deux fonctions suivantes peuvent être utilisées pour personnaliser
l'implémentation de l'observateur de processus enfant utilisé par la
boucle d'événements *asyncio* :

asyncio.get_child_watcher()

   Renvoie l'observateur enfant actuel pour la stratégie actuelle.

asyncio.set_child_watcher(watcher)

   Définit l'observateur enfant actuel à *watcher* pour la stratégie
   actuelle. *watcher* doit implémenter les méthodes définies dans la
   classe de base "AbstractChildWatcher".

Note:

  Les implémentations de boucles d'événement tierces peuvent ne pas
  prendre en charge les observateurs enfants personnalisés. Pour ces
  boucles d'événements, utiliser "set_child_watcher()" pourrait être
  interdit ou n'avoir aucun effet.

class asyncio.AbstractChildWatcher

   add_child_handler(pid, callback, *args)

      Enregistre un nouveau gestionnaire.

      Organise l'appel de "callback(pid, returncode, * args)"
      lorsqu'un processus dont le PID est égal à *pid* se termine. La
      spécification d'un autre rappel pour le même processus remplace
      le gestionnaire précédent.

      L'appelable *callback* doit être compatible avec les programmes
      à fils d'exécution multiples.

   remove_child_handler(pid)

      Supprime le gestionnaire de processus avec un PID égal à *pid*.

      La fonction renvoie "True" si le gestionnaire a été supprimé
      avec succès, "False" s'il n'y a rien à supprimer.

   attach_loop(loop)

      Attache l'observateur à une boucle d'événement.

      Si l'observateur était précédemment attaché à une boucle
      d'événements, il est d'abord détaché avant d'être rattaché à la
      nouvelle boucle.

      Remarque : la boucle peut être "None".

   is_active()

      Return "True" if the watcher is ready to use.

      Spawning a subprocess with *inactive* current child watcher
      raises "RuntimeError".

      Nouveau dans la version 3.8.

   close()

      Ferme l'observateur.

      Cette méthode doit être appelée pour s'assurer que les
      ressources sous-jacentes sont nettoyées.

class asyncio.ThreadedChildWatcher

   This implementation starts a new waiting thread for every
   subprocess spawn.

   It works reliably even when the asyncio event loop is run in a non-
   main OS thread.

   There is no noticeable overhead when handling a big number of
   children (*O(1)* each time a child terminates), but starting a
   thread per process requires extra memory.

   This watcher is used by default.

   Nouveau dans la version 3.8.

class asyncio.MultiLoopChildWatcher

   This implementation registers a "SIGCHLD" signal handler on
   instantiation. That can break third-party code that installs a
   custom handler for "SIGCHLD" signal.

   The watcher avoids disrupting other code spawning processes by
   polling every process explicitly on a "SIGCHLD" signal.

   There is no limitation for running subprocesses from different
   threads once the watcher is installed.

   The solution is safe but it has a significant overhead when
   handling a big number of processes (*O(n)* each time a "SIGCHLD" is
   received).

   Nouveau dans la version 3.8.

class asyncio.SafeChildWatcher

   This implementation uses active event loop from the main thread to
   handle "SIGCHLD" signal. If the main thread has no running event
   loop another thread cannot spawn a subprocess ("RuntimeError" is
   raised).

   The watcher avoids disrupting other code spawning processes by
   polling every process explicitly on a "SIGCHLD" signal.

   This solution is as safe as "MultiLoopChildWatcher" and has the
   same *O(N)* complexity but requires a running event loop in the
   main thread to work.

class asyncio.FastChildWatcher

   Cette implémentation récupère tous les processus terminés en
   appelant directement "os.waitpid(-1)", cassant éventuellement un
   autre code qui génère des processus et attend leur fin.

   Il n'y a pas de surcharge visible lors de la manipulation d'un
   grand nombre d'enfants (*O(1)* à chaque fois qu'un enfant se
   termine).

   This solution requires a running event loop in the main thread to
   work, as "SafeChildWatcher".

class asyncio.PidfdChildWatcher

   This implementation polls process file descriptors (pidfds) to
   await child process termination. In some respects,
   "PidfdChildWatcher" is a "Goldilocks" child watcher implementation.
   It doesn't require signals or threads, doesn't interfere with any
   processes launched outside the event loop, and scales linearly with
   the number of subprocesses launched by the event loop. The main
   disadvantage is that pidfds are specific to Linux, and only work on
   recent (5.3+) kernels.

   Nouveau dans la version 3.9.


Stratégies personnalisées
=========================

Pour implémenter une nouvelle politique de boucle d’événements, il est
recommandé de sous-classer "DefaultEventLoopPolicy" et de
réimplémenter les méthodes pour lesquelles un comportement
personnalisé est souhaité, par exemple :

   class MyEventLoopPolicy(asyncio.DefaultEventLoopPolicy):

       def get_event_loop(self):
           """Get the event loop.

           This may be None or an instance of EventLoop.
           """
           loop = super().get_event_loop()
           # Do something with loop ...
           return loop

   asyncio.set_event_loop_policy(MyEventLoopPolicy())
