Saltar al contenido
> 💻 🧠 Código 1001 > 📚 Materiales de Aprendizaje > 🐍 Python > 🧲 Cómo Capturar print() en Python: Capturando stdout Sin Bibliotecas Externas

🧲 Cómo Capturar print() en Python: Capturando stdout Sin Bibliotecas Externas

  • por

🧩 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 como as 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 / EscenarioContext Manager Recomendado
Capturar solo printcapture_stdout
Capturar solo errores/excepcionescapture_stderr
Capturar stdout y stderr simultáneamentecapture_output
Capturar y mostrar al mismo tiempocapture_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.StringIO puede 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.stdout y sys.stderr son 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.stderr en finally

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() o capture_output()
  • Depuración manual → modo Tee
  • Comprueba contenido del buffer, no efectos secundarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *