En Python, puedes interceptar lo que se imprime en pantalla usando print().
Esto es útil para tests, depuración, logging o para suprimir salidas “ruidosas”.
Te mostraré cómo hacerlo elegantemente — usando solo la biblioteca estándar y unas pocas líneas de código.
In Questo Articolo
🧩 Entendiendo los Context Managers en Python
¿Qué es un Context Manager?
Un context manager gestiona recursos de manera segura y ordenada.
Define un comportamiento de inicialización y limpieza alrededor de un bloque de código.
Ejemplo: manejo de archivos
with open("file.txt", "w") as f:
f.write("¡Hola, mundo!")
✅ Limpieza automática: el archivo se cierra incluso si ocurre una excepción.
Por Qué Son Útiles los Context Managers
- Limpieza automática – los recursos se liberan solos
- Seguridad ante excepciones – la limpieza se ejecuta incluso si hay errores
- Código más legible – encapsula la lógica de setup/teardown
- Reutilizable – funciona con archivos, locks, conexiones de red o stdout
Uso de @contextmanager de contextlib
El decorador @contextmanager permite crear context managers usando un generador:
from contextlib import contextmanager
@contextmanager
def my_context():
print("Entrando al contexto")
resource = "algún recurso"
try:
yield resource
finally:
print("Saliendo del contexto")
with my_context() as res:
print("Dentro del contexto:", res)
Salida:
Entrando al contexto
Dentro del contexto: algún recurso
Saliendo del contexto
💡 Explicación:
- Código antes de
yield→ se ejecuta al entrar - Valor pasado por
yield→ disponible comoas res - Código después de
yield→ se ejecuta al salir, incluso si hay excepciones
Esto es perfecto para cambiar temporalmente el estado global, como capturar stdout.
⚡ Resumen: Context Managers para stdout/stderr
| Tarea / Escenario | Context Manager Recomendado |
|---|---|
Capturar solo print | capture_stdout |
| Capturar solo errores/excepciones | capture_stderr |
| Capturar stdout y stderr simultáneamente | capture_output |
| Capturar y mostrar al mismo tiempo | capture_stdout_tee / capture_output_tee |
📦 Capturando 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
Ejemplo:
with capture_stdout() as out:
print("Esta salida fue capturada")
print("Capturado:", out.getvalue())
🔧 Capturando stderr o Ambos Flujos
Capturar stderr
@contextmanager
def capture_stderr():
old_stderr = sys.stderr
sys.stderr = buffer = io.StringIO()
try:
yield buffer
finally:
sys.stderr = old_stderr
Capturar 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
🔀 Modo Tee: Capturar Y Mostrar
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
Ejemplo:
with capture_stdout_tee() as out:
print("Visible Y capturado")
print("Contenido Tee:", out.getvalue())
📦 Archivo Completo: stdout_capture.py
"""
Utilidad para capturar stdout y stderr con context managers.
Soporta:
- captura de stdout
- captura de stderr
- captura combinada
- modo 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
✅ Ejemplos de Uso
from stdout_capture import capture_stdout, capture_output, capture_stdout_tee
def test_func():
print("Hola desde la función")
# Capturar stdout
with capture_stdout() as out:
test_func()
print("stdout capturado:", out.getvalue())
# Capturar stdout + stderr
try:
with capture_output() as out:
print("Algo")
raise Exception("¡Ups!")
except Exception:
pass
print("Salida capturada:", out.getvalue())
# Capturar y mostrar simultáneamente
with capture_stdout_tee() as out:
print("Visible Y capturado")
print("Contenido Tee:", out.getvalue())
⚠️ Consejos Prácticos y Advertencias
1️⃣ Salida muy grande
io.StringIOpuede usar mucha memoria con grandes volúmenes.- Usa un archivo temporal:
import sys, tempfile
with tempfile.TemporaryFile(mode='w+') as tmp:
old_stdout = sys.stdout
sys.stdout = tmp
try:
print("Muchos datos...")
tmp.seek(0)
captured = tmp.read()
finally:
sys.stdout = old_stdout
2️⃣ Salida de subprocesos
capture_stdout()no captura la salida de subprocesos.- Usa
subprocess.PIPE:
import subprocess
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
print(result.stdout)
3️⃣ Seguridad en threads
sys.stdoutysys.stderrson globales → evita capturas concurrentes en múltiples threads.
4️⃣ Cuándo usar Tee
- Depurar funciones mientras se hace logging
- Herramientas CLI interactivas
- Tests de integración con salida a terminal
5️⃣ Restaurar Streams
- Siempre restaura
sys.stdout/sys.stderrenfinally
6️⃣ Evitar demasiada anidación
- Varias capturas anidadas pueden mezclar la salida. Usa un solo contexto por escenario.
7️⃣ Buenas prácticas en tests
- Unit tests →
capture_stdout()ocapture_output() - Depuración manual → modo Tee
- Comprueba contenido del buffer, no efectos secundarios