Files
docs-fastapi/docs/fr/docs/advanced/events.md
T

166 lines
9.2 KiB
Markdown

# Événements de cycle de vie { #lifespan-events }
Vous pouvez définir une logique (du code) qui doit être exécutée avant que l'application ne **démarre**. Cela signifie que ce code sera exécuté **une seule fois**, **avant** que l'application ne **commence à recevoir des requêtes**.
De la même manière, vous pouvez définir une logique (du code) qui doit être exécutée lorsque l'application **s'arrête**. Dans ce cas, ce code sera exécuté **une seule fois**, **après** avoir traité potentiellement **de nombreuses requêtes**.
Comme ce code est exécuté avant que l'application ne **commence** à recevoir des requêtes, et juste après qu'elle **termine** de les traiter, il couvre tout le **cycle de vie** de l'application (le mot « lifespan » va être important dans un instant 😉).
Cela peut être très utile pour configurer des **ressources** dont vous avez besoin pour l'ensemble de l'application, qui sont **partagées** entre les requêtes, et/ou que vous devez **nettoyer** ensuite. Par exemple, un pool de connexions à une base de données, ou le chargement d'un modèle d'apprentissage automatique partagé.
## Cas d'utilisation { #use-case }
Commençons par un exemple de **cas d'utilisation**, puis voyons comment le résoudre avec ceci.
Imaginons que vous ayez des **modèles d'apprentissage automatique** que vous souhaitez utiliser pour traiter des requêtes. 🤖
Les mêmes modèles sont partagés entre les requêtes, ce n'est donc pas un modèle par requête, ni un par utilisateur, ou quelque chose de similaire.
Imaginons que le chargement du modèle puisse **prendre pas mal de temps**, car il doit lire beaucoup de **données depuis le disque**. Vous ne voulez donc pas le faire pour chaque requête.
Vous pourriez le charger au niveau supérieur du module/fichier, mais cela signifierait aussi qu'il **chargerait le modèle** même si vous exécutez simplement un test automatisé simple ; ce test serait alors **lent** car il devrait attendre le chargement du modèle avant de pouvoir exécuter une partie indépendante du code.
C'est ce que nous allons résoudre : chargeons le modèle avant que les requêtes ne soient traitées, mais seulement juste avant que l'application ne commence à recevoir des requêtes, pas pendant le chargement du code.
## Cycle de vie { #lifespan }
Vous pouvez définir cette logique de *démarrage* et d'*arrêt* en utilisant le paramètre `lifespan` de l'application `FastAPI`, et un « gestionnaire de contexte » (je vais vous montrer ce que c'est dans un instant).
Commençons par un exemple, puis voyons-le en détail.
Nous créons une fonction async `lifespan()` avec `yield` comme ceci :
{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *}
Ici, nous simulons l'opération de *démarrage* coûteuse de chargement du modèle en plaçant la fonction (factice) du modèle dans le dictionnaire avec les modèles d'apprentissage automatique avant le `yield`. Ce code sera exécuté **avant** que l'application ne **commence à recevoir des requêtes**, pendant le *démarrage*.
Puis, juste après le `yield`, nous déchargeons le modèle. Ce code sera exécuté **après** que l'application **a fini de traiter les requêtes**, juste avant l'*arrêt*. Cela pourrait, par exemple, libérer des ressources comme la mémoire ou un GPU.
/// tip | Astuce
Le `shutdown` se produit lorsque vous **arrêtez** l'application.
Peut-être devez-vous démarrer une nouvelle version, ou vous en avez simplement assez de l'exécuter. 🤷
///
### Fonction de cycle de vie { #lifespan-function }
La première chose à remarquer est que nous définissons une fonction async avec `yield`. C'est très similaire aux Dépendances avec `yield`.
{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *}
La première partie de la fonction, avant le `yield`, sera exécutée **avant** le démarrage de l'application.
Et la partie après le `yield` sera exécutée **après** que l'application a terminé.
### Gestionnaire de contexte asynchrone { #async-context-manager }
Si vous regardez, la fonction est décorée avec `@asynccontextmanager`.
Cela convertit la fonction en quelque chose appelé un « **gestionnaire de contexte asynchrone** ».
{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *}
Un **gestionnaire de contexte** en Python est quelque chose que vous pouvez utiliser dans une instruction `with`. Par exemple, `open()` peut être utilisé comme gestionnaire de contexte :
```Python
with open("file.txt") as file:
file.read()
```
Dans les versions récentes de Python, il existe aussi un **gestionnaire de contexte asynchrone**. Vous l'utiliseriez avec `async with` :
```Python
async with lifespan(app):
await do_stuff()
```
Quand vous créez un gestionnaire de contexte ou un gestionnaire de contexte asynchrone comme ci-dessus, ce qu'il fait, c'est qu'avant d'entrer dans le bloc `with`, il exécute le code avant le `yield`, et après être sorti du bloc `with`, il exécute le code après le `yield`.
Dans notre exemple de code ci-dessus, nous ne l'utilisons pas directement, mais nous le transmettons à FastAPI pour qu'il l'utilise.
Le paramètre `lifespan` de l'application `FastAPI` accepte un **gestionnaire de contexte asynchrone**, nous pouvons donc lui passer notre nouveau gestionnaire de contexte asynchrone `lifespan`.
{* ../../docs_src/events/tutorial003_py310.py hl[22] *}
## Événements alternatifs (déprécié) { #alternative-events-deprecated }
/// warning | Alertes
La méthode recommandée pour gérer le *démarrage* et l'*arrêt* est d'utiliser le paramètre `lifespan` de l'application `FastAPI` comme décrit ci-dessus. Si vous fournissez un paramètre `lifespan`, les gestionnaires d'événements `startup` et `shutdown` ne seront plus appelés. C'est soit tout en `lifespan`, soit tout en événements, pas les deux.
Vous pouvez probablement passer cette partie.
///
Il existe une autre manière de définir cette logique à exécuter au *démarrage* et à l'*arrêt*.
Vous pouvez définir des gestionnaires d'événements (fonctions) qui doivent être exécutés avant le démarrage de l'application, ou lorsque l'application s'arrête.
Ces fonctions peuvent être déclarées avec `async def` ou un `def` normal.
### Événement `startup` { #startup-event }
Pour ajouter une fonction qui doit être exécutée avant le démarrage de l'application, déclarez-la avec l'événement `"startup"` :
{* ../../docs_src/events/tutorial001_py310.py hl[8] *}
Dans ce cas, la fonction gestionnaire de l'événement `startup` initialisera la « base de données » des items (juste un `dict`) avec quelques valeurs.
Vous pouvez ajouter plusieurs fonctions de gestion d'événements.
Et votre application ne commencera pas à recevoir des requêtes avant que tous les gestionnaires de l'événement `startup` aient terminé.
### Événement `shutdown` { #shutdown-event }
Pour ajouter une fonction qui doit être exécutée lorsque l'application s'arrête, déclarez-la avec l'événement `"shutdown"` :
{* ../../docs_src/events/tutorial002_py310.py hl[6] *}
Ici, la fonction gestionnaire de l'événement `shutdown` écrira une ligne de texte `"Application shutdown"` dans un fichier `log.txt`.
/// note | Remarque
Dans la fonction `open()`, le `mode="a"` signifie « append » (ajouter) ; la ligne sera donc ajoutée après ce qui se trouve déjà dans ce fichier, sans écraser le contenu précédent.
///
/// tip | Astuce
Notez que dans ce cas, nous utilisons une fonction Python standard `open()` qui interagit avec un fichier.
Cela implique des E/S (input/output), qui nécessitent « d'attendre » que des choses soient écrites sur le disque.
Mais `open()` n'utilise pas `async` et `await`.
Nous déclarons donc la fonction gestionnaire d'événement avec un `def` standard plutôt qu'avec `async def`.
///
### `startup` et `shutdown` ensemble { #startup-and-shutdown-together }
Il y a de fortes chances que la logique de votre *démarrage* et de votre *arrêt* soit liée : vous pourriez vouloir démarrer quelque chose puis le terminer, acquérir une ressource puis la libérer, etc.
Faire cela dans des fonctions séparées qui ne partagent pas de logique ni de variables est plus difficile, car vous devriez stocker des valeurs dans des variables globales ou recourir à des astuces similaires.
Pour cette raison, il est désormais recommandé d'utiliser plutôt le `lifespan` comme expliqué ci-dessus.
## Détails techniques { #technical-details }
Juste un détail technique pour les nerds curieux. 🤓
Sous le capot, dans la spécification technique ASGI, cela fait partie du [protocole Lifespan](https://asgi.readthedocs.io/en/latest/specs/lifespan.html), et il y définit des événements appelés `startup` et `shutdown`.
/// note | Remarque
Vous pouvez en lire plus sur les gestionnaires `lifespan` de Starlette dans les [documents « Lifespan » de Starlette](https://starlette.dev/lifespan/).
Y compris comment gérer l'état de cycle de vie qui peut être utilisé dans d'autres parties de votre code.
///
## Sous-applications { #sub-applications }
🚨 Gardez à l'esprit que ces événements de cycle de vie (démarrage et arrêt) ne seront exécutés que pour l'application principale, pas pour [Sous-applications - Montages](sub-applications.md).