modifié le

Context manager

1. Utiliser un context manager

Un context manager permet de grouper des instructions, et d’exécuter automatiquement du code au préalable et a posteriori.

exemple de context manager
with open('data', 'w') as mon_fichier: (1)
  for i in range(-10,10):
      print(i, file=mon_fichier)
(2)
1 ici, le fichier est ouvert
2 en sortie de bloc, le fichier est automatiquement fermé

Ce code est à peu près équivalent à :

mon_fichier = open("data", "w")
for i in range(-10,10):
    print(i, file=mon_fichier)
mon_fichier.close()

Globalement, l’intérêt du context manager est de doter le traitement à réaliser d’un préambule et d’une conclusion, comme :

  • une ouverture / fermeture (fichiers, sockets, etc.)

  • un verrouillage / déverrouillage (fichiers, transactions, code, etc.)

  • une allocation / libération (mémoire, session, etc.)

  • une connexion / déconnexion (réseau, sgbd, etc.)

  • un démarrage / arrêt (service, etc.)

et d’assurer que la conclusion sera réalisée, quels que soient les évènements qui peuvent advenir.

2. Ecrire un context manager

Un contexte manager Cm est une classe possédant 2 méthodes particulières : __enter__() et __exit()__, correspondant au préambule et à la conclusion, et qui, de ce fait, s’utilise comme :

with Cm() : (1)
  (2)
  ...
  (3)
1 création d’un context_manager anonyme
2 exécution de Cm().__enter__()`
3 exécution de Cm().__exit__(…​)`

ou

with Cm() as mon_context_manager: (1)
  (2)
  ...
  (3)
1 création d’une instance mon_context_manager de type context_manager
2 exécution de mon_context_manager.__enter__()
3 exécution de mon_context_manager.__exit__(…​)

2.1. Méthodes __enter__() et __exit__()

__enter__(self)

Cette méthode implémente le préambule, et doit renvoyer ce que l’on veut récupérer dans la variable spécifiée après as.

__exit__(self,type,value,traceback)

Cette méthode implémente l’épilogue. Elle est définie avec 4 paramètres.

2.2. Arguments

Des arguments peuvent être spécifiés à la création du context manager :

with Cm(arg1,arg2) as mon_context_manager:
  ...

Ils sont alors récupérables via les paramètres du constructeur.

2.3. __exit__()

Cette méthode est déclarée comme __exit__(self,type,value,traceback).

Si l’on entre dans __exit__() en situation normale (c’est-à-dire parce que l’exécution du bloc se termine normalement), alors les paramètres n’ont pas d’intérêt (ils valent None).

Si l’on entre dans __exit__() parce qu’une exception a été lancée depuis le bloc (donc son exécution s’est terminée prématurément), alors on récupère dans les paramètres le type de l’exception (type), sa valeur (value) et un objet permettant d’accéder à la pile des appels en cours (traceback).

En sortant de __exit__(), si celle-ci renvoie True, alors l’exception est attrapée et supposée traitée (et par conséquent l’exécution du programme se poursuit normalement, après le context manager). Sinon (cas où __exit__() ne renvoie pas True) l’exception poursuit sa remontée.