El módulo warnings permite señalar comportamientos problemáticos sin detener inmediatamente la ejecución. Las bibliotecas lo usan para APIs obsoletas, combinaciones inseguras de argumentos, cambios futuros y prácticas que todavía funcionan pero deben corregirse. El problema aparece cuando el aviso nace dentro del paquete: la ubicación mostrada puede apuntar a un helper interno, aunque la línea que el usuario debe modificar esté en la aplicación. El parámetro skip_file_prefixes permite ignorar archivos internos al buscar el frame relevante de la pila.
Por qué importa la ubicación
Un warning útil debe explicar qué ocurrió, por qué merece atención y dónde corregirlo. Si la línea indicada pertenece a una función privada, el desarrollador debe inspeccionar la pila manualmente. Decorators, wrappers, adaptadores y capas de compatibilidad aumentan esa dificultad. El parámetro tradicional stacklevel puede subir un número fijo de frames, pero una refactorización interna cambia la profundidad y vuelve frágil ese valor.
Con skip_file_prefixes, la biblioteca declara qué prefijos de rutas son internos. Python recorre la pila y selecciona un archivo fuera de esas rutas. La regla describe la intención y no depende de un número mágico.
Ejemplo básico
from pathlib import Path
import warnings
RAIZ_PAQUETE = str(Path(__file__).resolve().parent)
def funcion_antigua(valor):
warnings.warn(
"funcion_antigua() está obsoleta; usa funcion_nueva()",
DeprecationWarning,
skip_file_prefixes=(RAIZ_PAQUETE,),
)
return valor
Cuando una aplicación llama a funcion_antigua(), el aviso puede apuntar a la línea de la aplicación y no a la implementación de la biblioteca. El argumento acepta una tupla porque un proyecto puede tener más de una raíz interna.
stacklevel frente a skip_file_prefixes
stacklevel sigue siendo útil en cadenas simples. Un valor 2 suele mostrar al llamador; 3 sube un frame adicional. Sin embargo, wrappers condicionales y decorators cambian la profundidad. Una modificación interna puede hacer que el warning apunte al archivo equivocado. Los prefijos son más estables porque siguen siendo válidos aunque el paquete añada capas.
warnings.warn(
"Opción heredada",
FutureWarning,
stacklevel=2,
)
En una biblioteca pequeña y directa, stacklevel=2 puede bastar. En frameworks o paquetes con varias capas, ignorar la raíz del paquete suele ser más mantenible.
Elegir el prefijo correcto
Usa rutas absolutas y normalizadas:
from pathlib import Path
_PREFIJO_INTERNO = str(Path(__file__).resolve().parent)
Si el proyecto contiene varios subpaquetes bajo una raíz común, utiliza esa raíz instalada. Evita prefijos demasiado amplios como /usr o la raíz de una unidad, porque podrían ocultar frames externos legítimos. resolve() reduce diferencias causadas por rutas relativas o enlaces simbólicos.
La guía de Academify sobre pathlib en Python explica el manejo portátil de rutas. Para comprender la introspección de la pila, consulta inspect en Python.
Avisos de obsolescencia
Usa DeprecationWarning para APIs dirigidas principalmente a desarrolladores y consumidores de bibliotecas. FutureWarning suele ser más visible y puede ser adecuado cuando el cambio afecta directamente a aplicaciones finales. El mensaje debe indicar el comportamiento obsoleto, la alternativa y, cuando sea posible, la versión prevista de eliminación.
def cargar_config(ruta, *, modo=None):
if modo == "legacy":
warnings.warn(
"modo='legacy' será eliminado; usa modo='safe'",
FutureWarning,
skip_file_prefixes=(_PREFIJO_INTERNO,),
)
Los mensajes vagos crean ruido. Los mensajes accionables facilitan la migración y reducen dudas.
Decorators y wrappers
Los decorators son un caso ideal para esta opción porque cada wrapper añade un frame. Registro, telemetría, validación y compatibilidad pueden quedar entre el usuario y la llamada a warn(). Al ignorar todo el directorio interno, Python encuentra con mayor precisión el código consumidor.
from functools import wraps
def obsoleta(func):
@wraps(func)
def wrapper(*args, **kwargs):
warnings.warn(
f"{func.__name__} está obsoleta",
DeprecationWarning,
skip_file_prefixes=(_PREFIJO_INTERNO,),
)
return func(*args, **kwargs)
return wrapper
Lee el artículo sobre decorators en Python para entender cómo los wrappers modifican la pila.
Probar la ubicación reportada
No pruebes solo el texto. Captura warnings y verifica archivo, línea y categoría. El contexto catch_warnings() permite aislar la prueba:
import warnings
def test_warning_apunta_al_consumidor():
with warnings.catch_warnings(record=True) as capturados:
warnings.simplefilter("always")
funcion_antigua(10)
aviso = capturados[0]
assert "test_" in aviso.filename
assert aviso.category is DeprecationWarning
Las pruebas automatizadas protegen el comportamiento cuando cambia la estructura interna. La guía de pytest en Python ayuda a organizar estos casos.
Filtros y política de la aplicación
La aplicación controla los warnings mediante la opción -W, la variable PYTHONWARNINGS y funciones como simplefilter() y filterwarnings(). Una biblioteca no debería modificar filtros globales durante la importación, porque afectaría a todo el proceso. La biblioteca emite el aviso correcto y la aplicación decide si lo muestra, ignora, registra o convierte en excepción.
python -W error::DeprecationWarning -m pytest
Convertir avisos de obsolescencia en errores dentro de integración continua ayuda a detectar dependencias antiguas antes de una actualización.
Compatibilidad con versiones anteriores
Comprueba la versión mínima compatible antes de depender de skip_file_prefixes. Un paquete que aún soporta versiones anteriores puede usar stacklevel como fallback:
import sys
opciones = {}
if sys.version_info >= (3, 12):
opciones["skip_file_prefixes"] = (_PREFIJO_INTERNO,)
else:
opciones["stacklevel"] = 2
warnings.warn("API obsoleta", DeprecationWarning, **opciones)
Centraliza esta lógica en una función auxiliar para que todos los avisos sigan la misma política. Cuando termine el soporte para versiones antiguas, eliminar el fallback será sencillo.
Varias raíces internas
Algunos proyectos cargan código desde un directorio generado, una dependencia vendorizada o un puente de plugins. Pasa varios prefijos cuando todos sean detalles de implementación:
INTERNOS = (
str(RAIZ_PAQUETE),
str(RAIZ_GENERADA),
)
warnings.warn(
"La capa de compatibilidad está obsoleta",
DeprecationWarning,
skip_file_prefixes=INTERNOS,
)
Mantén la lista específica y prueba varias entradas públicas. Un prefijo demasiado amplio puede saltar el consumidor real y mostrar un frame exterior sin relación.
Rendimiento y repetición
Los warnings no son excepciones, pero emitirlos miles de veces dentro de un bucle añade coste y ruido. Emite el aviso cuando se detecte por primera vez la condición o deja que los filtros estándar supriman repeticiones de la misma ubicación. No uses warnings para errores que invalidan la operación. Datos corruptos, autenticación fallida o recursos obligatorios ausentes deben producir excepciones.
Diseñar una política de biblioteca
Un paquete maduro debería definir categorías, formato de mensajes, período de migración y reglas de eliminación. Documenta las obsolescencias en las notas de versión y ofrece una alternativa funcional antes de retirar la API. Prueba tanto el warning como el camino recomendado. Una ubicación exacta vuelve la política más efectiva porque el usuario llega directamente al código que controla.
Conclusión
skip_file_prefixes mejora los warnings al dirigir al desarrollador al código consumidor, no a helpers internos. Es más resistente que una profundidad fija, especialmente con wrappers y decorators. Usa rutas resueltas y específicas, mensajes accionables, categorías correctas y pruebas automáticas. Consulta la documentación oficial de warnings y la PEP 565 para profundizar en la visibilidad y las reglas de obsolescencia.







