catch_warnings: captura warnings en pruebas Python

Publicado el: 07/10/2026
Tempo de leitura: 5 minutos
Código Python mostrando avisos controlados con catch_warnings

El contexto warnings.catch_warnings permite controlar temporalmente los avisos de Python en pruebas, bibliotecas, scripts de migración e integraciones con código heredado. En lugar de cambiar filtros de forma permanente, guarda el estado actual, aplica reglas dentro de un bloque y restaura la configuración anterior al finalizar. Esto permite capturar, convertir, ignorar o validar avisos sin afectar otras partes de la aplicación.

Los avisos no son excepciones. Señalan situaciones que merecen atención, como APIs obsoletas, comportamientos que cambiarán, posible pérdida de precisión o uso inadecuado de recursos. El programa normalmente continúa. Por eso, una prueba madura debe verificar no solo el resultado, sino también los avisos emitidos.

Uso básico

import warnings

with warnings.catch_warnings(record=True) as captured:
    warnings.simplefilter("always")
    warnings.warn("función antigua", DeprecationWarning)

assert len(captured) == 1
assert captured[0].category is DeprecationWarning
assert "función antigua" in str(captured[0].message)

Con record=True, el contexto devuelve una lista de objetos WarningMessage. Cada elemento contiene mensaje, categoría, archivo, línea y otros datos. El filtro always es importante en pruebas porque Python puede ocultar avisos repetidos mediante su registro interno.

Por qué importa restaurar el estado

Funciones como warnings.simplefilter y warnings.filterwarnings modifican el estado del módulo. Si una prueba ignora todos los avisos y no restaura la configuración, otras pruebas pueden pasar incorrectamente. El contexto reduce ese riesgo y restaura los filtros incluso si ocurre una excepción.

La restauración no significa que todo uso sea automáticamente seguro con concurrencia. La configuración puede implicar estado compartido. En programas con threads o tareas asíncronas, evita contextos amplios mientras otras partes cambian filtros. Prefiere bloques breves, pruebas aisladas y políticas definidas al iniciar la aplicación.

Capturar una categoría específica

import warnings

def legacy_api():
    warnings.warn(
        "legacy_api será eliminada",
        DeprecationWarning,
        stacklevel=2,
    )
    return 42

with warnings.catch_warnings(record=True) as captured:
    warnings.simplefilter("always", DeprecationWarning)
    result = legacy_api()

assert result == 42
assert len(captured) == 1
assert issubclass(captured[0].category, DeprecationWarning)

Filtrar por categoría es mejor que ocultar todo. Una regla global con ignore puede esconder ResourceWarning, RuntimeWarning u otros avisos importantes. Una regla específica expresa mejor la intención.

Convertir avisos en errores

En pruebas e integración continua puede ser útil convertir avisos seleccionados en excepciones. Así el equipo corrige APIs obsoletas antes de que una actualización rompa el sistema.

with warnings.catch_warnings():
    warnings.simplefilter("error", DeprecationWarning)
    legacy_api()

La llamada genera una excepción DeprecationWarning. Esta política funciona bien en una suite controlada, pero aplicarla sin límites en producción puede interrumpir peticiones por avisos de dependencias externas. Empieza por categorías y módulos concretos.

Filtros por mensaje y módulo

filterwarnings permite combinar acción, expresión regular del mensaje, categoría, módulo y número de línea.

with warnings.catch_warnings(record=True) as captured:
    warnings.filterwarnings(
        "always",
        message=r".*parámetro antiguo.*",
        category=DeprecationWarning,
        module=r"mi_paquete\..*",
    )
    ejecutar_flujo()

Usa expresiones simples y estables. Una prueba ligada al texto completo de una biblioteca puede romperse por un cambio editorial. Es mejor validar categoría, fragmento relevante y origen.

El papel de stacklevel

Al emitir un aviso desde una biblioteca, configura stacklevel para que apunte al código del usuario y no a la línea interna que llama a warnings.warn. Normalmente stacklevel=2 apunta un nivel arriba, aunque wrappers adicionales pueden requerir otro valor.

def nuevo_nombre():
    return 10

def nombre_antiguo():
    warnings.warn(
        "usa nuevo_nombre()",
        DeprecationWarning,
        stacklevel=2,
    )
    return nuevo_nombre()

Un aviso bien ubicado reduce el tiempo de corrección. Las pruebas pueden validar filename y lineno cuando la ubicación forma parte del contrato.

Registro interno de avisos

Python mantiene registros para evitar repetir ciertos mensajes. Por eso un aviso puede aparecer una vez y desaparecer después. En pruebas, simplefilter("always") dentro de catch_warnings hace el comportamiento más predecible. Aun así, módulos ya importados pueden conservar registros propios, por lo que no conviene depender del orden de ejecución.

Integración con logging

logging.captureWarnings(True) redirige avisos al sistema de logs. Esto es útil en servicios, pero es diferente de capturar una lista para aserciones. En pruebas unitarias, catch_warnings(record=True) suele ser más directo. En producción, logging facilita timestamps, correlación y almacenamiento centralizado.

Buenas prácticas para bibliotecas

Elige categorías con intención. DeprecationWarning comunica eliminaciones futuras a desarrolladores; FutureWarning puede servir para cambios que afectan a usuarios finales; categorías personalizadas ayudan a separar dominios. Documenta la versión de introducción, la alternativa recomendada y la fecha prevista de retirada.

No uses avisos cuando el resultado sea inválido; en ese caso lanza una excepción. Tampoco emitas el mismo aviso en bucles intensivos sin necesidad, porque añade ruido y coste.

Probar que no hay avisos inesperados

with warnings.catch_warnings(record=True) as captured:
    warnings.simplefilter("always")
    ejecutar_operacion_estable()

unexpected = [w for w in captured if w.category is not UserWarning]
assert not unexpected

Este patrón permite aceptar una categoría conocida y rechazar otras. En proyectos grandes, crea funciones auxiliares para estandarizar filtros y mensajes de error.

Concurrencia y alcance

El principal riesgo operativo es asumir que los filtros son locales mientras otro flujo modifica el mismo estado. Mantén el contexto lo más corto posible y evita incluir operaciones largas de red, esperas o procesamiento paralelo. En servidores, define la política general al iniciar y reserva la captura temporal para pruebas o tareas controladas.

Cuándo usar catch_warnings

Úsalo para probar deprecaciones, validar bibliotecas, silenciar un aviso conocido en un bloque pequeño, convertir categorías concretas en error o inspeccionar origen y mensaje. No lo uses solo para “limpiar” la terminal. Un aviso repetido suele indicar una dependencia desactualizada, una API antigua o un comportamiento que debe corregirse.

Consulta la documentación oficial de warnings y la sección sobre integración con logging. En Academify, continúa con los contenidos sobre Python, pruebas en Python, programación y el curso de Python.

Conclusión

warnings.catch_warnings ofrece control temporal y verificable sobre los avisos. Un uso robusto combina filtros específicos, bloques breves, record=True en pruebas, stacklevel correcto al emitir mensajes y cautela con el estado compartido. Así, los avisos dejan de ser ruido y se convierten en una herramienta de calidad, compatibilidad y mantenimiento.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código Python que representa referencias persistentes de pickle
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serializa referencias externas

    Aprende pickle persistent_id en Python para referencias externas estables, validación, seguridad, rendimiento y compatibilidad.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binario que representa buffers y vistas de memoria en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: cuenta valores sin copiar buffers

    Aprende memoryview.count en Python para contar bytes y valores en buffers sin copias, con formatos, límites y buenas prácticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python y anotaciones de tipos
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evita imports circulares en anotaciones

    Aprende annotationlib en Python para inspeccionar anotaciones diferidas, evitar imports circulares y crear herramientas seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python en pantalla que representa inspección de módulos y paquetes
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    inspect.ispackage: identifica paquetes Python

    Aprende inspect.ispackage en Python para identificar paquetes, explorar módulos y crear herramientas de introspección seguras.

    Ler mais

    Tempo de leitura: 5 minutos
    05/10/2026
    Portátil con código y gráficos de rendimiento para analizar sys._jit en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sys._jit: detecta y mide el JIT experimental

    Aprende sys._jit en Python para detectar soporte JIT experimental, medir rendimiento y evitar decisiones frágiles.

    Ler mais

    Tempo de leitura: 5 minutos
    05/10/2026
    Visualización de precisión numérica para cálculos con math.fma en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    math.fma: cálculo con un único redondeo

    Aprende math.fma en Python para multiplicar y sumar con un único redondeo y mejorar la estabilidad numérica.

    Ler mais

    Tempo de leitura: 7 minutos
    04/10/2026