Skip to content
> 💻 🧠 Codice 1001 > 📚 Materiali di Apprendimento > 🐍 Python > 🧲 Come “catturare” print() in Python: intercettare stdout senza librerie esterne

🧲 Come “catturare” print() in Python: intercettare stdout senza librerie esterne


🧩 Comprendere i Context Manager in Python

Cos’è un Context Manager?

Un Context Manager gestisce le risorse in modo sicuro e ordinato.
Definisce il comportamento durante l’inizializzazione e la pulizia di un blocco di codice.

Esempio: lavorare con i file

with open("file.txt", "w") as f:
    f.write("Ciao, mondo!")

✅ Pulizia automatica: il file verrà chiuso anche se si verifica un’eccezione.


Perché i Context Manager sono utili

  • Pulizia automatica – le risorse vengono rilasciate automaticamente
  • Sicurezza in caso di errori – la pulizia avviene anche in caso di eccezioni
  • Leggibilità del codice – logica setup/teardown in un unico punto
  • Riutilizzabilità – adatto a file, locks, connessioni di rete o stdout

Uso di @contextmanager da contextlib

Il decoratore @contextmanager permette di creare context manager usando generatori:

from contextlib import contextmanager

@contextmanager
def my_context():
    print("Entriamo nel contesto")
    resource = "una risorsa"
    try:
        yield resource
    finally:
        print("Usciamo dal contesto")

with my_context() as res:
    print("Dentro il contesto:", res)

Output:

Entriamo nel contesto
Dentro il contesto: una risorsa
Usciamo dal contesto

💡 Spiegazione:

  • Codice prima di yield → eseguito all’ingresso
  • Valore di yield → disponibile come as res
  • Codice dopo yield → eseguito all’uscita, anche in caso di eccezione

Perfetto per modifiche temporanee dello stato globale, ad esempio per intercettare stdout.


⚡ Riepilogo: Context Manager per stdout/stderr

ScenarioContext Manager consigliato
Intercettare solo printcapture_stdout
Intercettare solo errori/exceptioncapture_stderr
Intercettare stdout e stderr insiemecapture_output
Intercettare e mostrare simultaneamentecapture_stdout_tee / capture_output_tee

📦 Intercettare 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

Esempio:

with capture_stdout() as out:
    print("Questa stampa è stata intercettata")

print("Intercettato:", out.getvalue())

🔧 Intercettare stderr o entrambi i flussi

Intercettare stderr

@contextmanager
def capture_stderr():
    old_stderr = sys.stderr
    sys.stderr = buffer = io.StringIO()
    try:
        yield buffer
    finally:
        sys.stderr = old_stderr

Intercettare 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

🔀 Modalità Tee: intercettare e mostrare

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 manager:

@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

Esempio:

with capture_stdout_tee() as out:
    print("Visibile e intercettato")

print("Contenuto Tee:", out.getvalue())

📦 File completo: stdout_capture.py

"""
Utility per intercettare stdout e stderr con context manager.
Supporta:
- intercettare stdout
- intercettare stderr
- intercettazione combinata
- modalità 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

✅ Esempi di utilizzo

from stdout_capture import capture_stdout, capture_output, capture_stdout_tee

def test_func():
    print("Ciao dalla funzione")

# Intercettare stdout
with capture_stdout() as out:
    test_func()
print("stdout intercettato:", out.getvalue())

# Intercettare stdout + stderr
try:
    with capture_output() as out:
        print("Qualcosa")
        raise Exception("Oops!")
except Exception:
    pass
print("Output intercettato:", out.getvalue())

# Intercettare e mostrare simultaneamente
with capture_stdout_tee() as out:
    print("Visibile E intercettato")
print("Contenuto Tee:", out.getvalue())

⚠️ Consigli pratici e avvertenze

1️⃣ Output molto grande

  • io.StringIO può consumare molta memoria con grandi quantità di dati
  • Usa un file temporaneo:
import sys, tempfile

with tempfile.TemporaryFile(mode='w+') as tmp:
    old_stdout = sys.stdout
    sys.stdout = tmp
    try:
        print("Molti dati...")
        tmp.seek(0)
        captured = tmp.read()
    finally:
        sys.stdout = old_stdout

2️⃣ Output dai subprocess

  • capture_stdout() non intercetta l’output di processi esterni
  • Usa subprocess.PIPE:
import subprocess
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
print(result.stdout)

3️⃣ Sicurezza nei thread

  • sys.stdout e sys.stderr sono globali → evitare di intercettare simultaneamente in più thread

4️⃣ Quando usare Tee

  • Debug di funzioni con logging
  • Strumenti CLI interattivi
  • Test di integrazione con output su terminale

5️⃣ Ripristinare i flussi

  • Ripristina sempre sys.stdout/sys.stderr nel finally

6️⃣ Evitare annidamenti profondi

  • Molti intercettamenti possono mischiare l’output. Usa un contesto per scenario.

7️⃣ Best Practices nei test

  • Unit test → capture_stdout() o capture_output()
  • Debug manuale → modalità Tee
  • Controlla il contenuto del buffer, non effetti collaterali

Se vuoi, posso anche preparare una versione interattiva HTML/JS, dove si può eseguire il codice direttamente nel browser e vedere l’output intercettato.

Vuoi che lo faccia?

Leave a Reply

Your email address will not be published. Required fields are marked *