En Python, vous pouvez intercepter ce qui est affiché à l’écran via print().
Ceci est utile pour les tests, le debugging, le logging, ou pour supprimer des sorties « bruyantes ».
Je vais montrer comment faire cela élégamment — en utilisant uniquement la bibliothèque standard et quelques lignes de code.
🧩 Comprendre les Context Managers en Python
Qu’est-ce qu’un Context Manager ?
Un context manager gère les ressources de manière sûre et ordonnée.
Il définit un comportement d’initialisation et de nettoyage autour d’un bloc de code.
Exemple : gestion de fichiers
with open("file.txt", "w") as f:
f.write("Bonjour, le monde !")
✅ Nettoyage automatique : le fichier est fermé même en cas d’exception.
Pourquoi les Context Managers sont utiles
- Nettoyage automatique – les ressources sont libérées automatiquement
- Sécurité en cas d’erreurs – le nettoyage s’exécute même si des exceptions surviennent
- Code plus lisible – encapsule la logique setup/teardown
- Réutilisable – fonctionne pour fichiers, verrous, connexions réseau ou stdout
Utilisation de @contextmanager depuis contextlib
Le décorateur @contextmanager permet de créer des context managers via un générateur :
from contextlib import contextmanager
@contextmanager
def my_context():
print("Entrée dans le contexte")
resource = "une ressource"
try:
yield resource
finally:
print("Sortie du contexte")
with my_context() as res:
print("Dans le contexte :", res)
Sortie :
Entrée dans le contexte
Dans le contexte : une ressource
Sortie du contexte
💡 Explication :
- Le code avant
yield→ s’exécute à l’entrée - La valeur passée par
yield→ disponible avecas res - Le code après
yield→ s’exécute à la sortie, même si une exception survient
C’est parfait pour modifier temporairement l’état global, comme capturer stdout.
⚡ Résumé : Context Managers pour stdout/stderr
| Tâche / Scénario | Context Manager recommandé |
|---|---|
Capturer uniquement print | capture_stdout |
| Capturer uniquement erreurs/exceptions | capture_stderr |
| Capturer stdout et stderr simultanément | capture_output |
| Capturer et afficher en même temps | capture_stdout_tee / capture_output_tee |
📦 Capturer stdout
from contextlib import contextmanager
import sys, io
@contextmanager
def capture_stdout():
old_stdout = sys.stdout
sys.stdout = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stdout = old_stdout
Exemple :
with capture_stdout() as out:
print("Cette sortie a été capturée")
print("Capturé :", out.getvalue())
🔧 Capturer stderr ou les deux flux
Capturer stderr
@contextmanager
def capture_stderr():
old_stderr = sys.stderr
sys.stderr = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stderr = old_stderr
Capturer stdout + stderr
@contextmanager
def capture_output():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
🔀 Mode Tee : Capturer ET Afficher
class Tee(io.StringIO):
def __init__(self, original):
super().__init__()
self.original = original
def write(self, text):
self.original.write(text)
super().write(text)
def flush(self):
self.original.flush()
Context managers :
@contextmanager
def capture_stdout_tee():
old_stdout = sys.stdout
sys.stdout = tee = Tee(old_stdout)
try:
yield tee
finally:
sys.stdout = old_stdout
@contextmanager
def capture_output_tee():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = tee = Tee(old_stdout)
try:
yield tee
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
Exemple :
with capture_stdout_tee() as out:
print("Visible ET capturé")
print("Contenu du Tee :", out.getvalue())
📦 Fichier Complet : stdout_capture.py
"""
Utilitaire pour capturer stdout et stderr avec des context managers.
Supporte :
- capture de stdout
- capture de stderr
- capture combinée
- mode Tee
"""
import sys, io
from contextlib import contextmanager
@contextmanager
def capture_stdout():
old_stdout = sys.stdout
sys.stdout = buffer = io.StringIO()
try: yield buffer
finally: sys.stdout = old_stdout
@contextmanager
def capture_stderr():
old_stderr = sys.stderr
sys.stderr = buffer = io.StringIO()
try: yield buffer
finally: sys.stderr = old_stderr
@contextmanager
def capture_output():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = buffer = io.StringIO()
try: yield buffer
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
class Tee(io.StringIO):
def __init__(self, original):
super().__init__()
self.original = original
def write(self, text):
self.original.write(text)
super().write(text)
def flush(self):
self.original.flush()
@contextmanager
def capture_stdout_tee():
old_stdout = sys.stdout
sys.stdout = tee = Tee(old_stdout)
try: yield tee
finally: sys.stdout = old_stdout
@contextmanager
def capture_output_tee():
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = sys.stderr = tee = Tee(old_stdout)
try: yield tee
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
✅ Exemples d’Utilisation
from stdout_capture import capture_stdout, capture_output, capture_stdout_tee
def test_func():
print("Bonjour depuis la fonction")
# Capturer stdout
with capture_stdout() as out:
test_func()
print("stdout capturé :", out.getvalue())
# Capturer stdout + stderr
try:
with capture_output() as out:
print("Quelque chose")
raise Exception("Oups !")
except Exception:
pass
print("Sortie capturée :", out.getvalue())
# Capturer et afficher simultanément
with capture_stdout_tee() as out:
print("Visible ET capturé")
print("Contenu Tee :", out.getvalue())
⚠️ Conseils Pratiques et Pièges
1️⃣ Sortie très volumineuse
io.StringIOpeut consommer beaucoup de mémoire pour de grandes sorties.- Utilisez un fichier temporaire :
import sys, tempfile
with tempfile.TemporaryFile(mode='w+') as tmp:
old_stdout = sys.stdout
sys.stdout = tmp
try:
print("Beaucoup de données...")
tmp.seek(0)
captured = tmp.read()
finally:
sys.stdout = old_stdout
2️⃣ Sortie des sous-processus
capture_stdout()ne capture pas la sortie des sous-processus.- Utilisez
subprocess.PIPE:
import subprocess
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
print(result.stdout)
3️⃣ Sécurité Threads
sys.stdoutetsys.stderrsont globaux → éviter les captures concurrentes sur plusieurs threads.
4️⃣ Quand utiliser Tee
- Débogage de fonctions avec logging
- Outils CLI interactifs
- Tests d’intégration avec sortie vers le terminal
5️⃣ Restaurer les flux
- Toujours restaurer
sys.stdout/sys.stderrdansfinally
6️⃣ Éviter les imbrications trop profondes
- Plusieurs captures imbriquées peuvent mélanger les sorties. Utilisez un seul contexte par scénario.
7️⃣ Bonnes pratiques pour les tests
- Unit tests →
capture_stdout()oucapture_output() - Débogage manuel → mode Tee
- Vérifier le contenu du buffer, pas les effets secondaires