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.







