Aller au contenu
> 💻 🧠 Code 1001 > 📚 Supports d'Apprentissage > 🐍 Python > 🧲 Comment Capturer print() en Python : Capturer stdout Sans Bibliothèques Externes

🧲 Comment Capturer print() en Python : Capturer stdout Sans Bibliothèques Externes

  • par

🧩 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 avec as 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énarioContext Manager recommandé
Capturer uniquement printcapture_stdout
Capturer uniquement erreurs/exceptionscapture_stderr
Capturer stdout et stderr simultanémentcapture_output
Capturer et afficher en même tempscapture_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.StringIO peut 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.stdout et sys.stderr sont 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.stderr dans finally

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() ou capture_output()
  • Débogage manuel → mode Tee
  • Vérifier le contenu du buffer, pas les effets secondaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *