Stacktrace para UserWarning

Veo una advertencia como esta en mis registros:

py.warnings.__init__: WARNING .../bs4/__init__.py:219: UserWarning: "foo" looks like a filename, not markup. You should probably open this file and pass the filehandle into Beautiful Soup 

Este mensaje no ayuda mucho.

Me gustaría ver el stacktrace donde esto sucede.

Por favor, no mire el contenido de esta advertencia. Esta pregunta no es sobre Beautiful Soup 🙂

Una solución fácil sería modificar el código de terceros ( bs4/__init__.py en la línea 219) y agregar algo como esto:

 import traceback logger.warn('Exc at ...\n%s' % ''.join(traceback.format_stack())) 

Pero me gustaría evitar esto. Razones:

  • Esta es una advertencia de un sistema de producción. No quiero cambiar la fuente.
  • La próxima vez que ocurra una advertencia como esta, me gustaría ver el seguimiento de stack inmediatamente

¿Hay alguna bandera o configuración para python que pueda cambiar, para ver no solo una línea, sino también el rastreo de stack? Necesito los marcos superiores para depurar esto.

En este entorno se utiliza Python 2.7.

Necesitarías hacer lo siguiente:

  1. Cree si USER_SITE no existe: emita python -c "import site; site._script()" , consulte el contenido de la variable USER_SITE
  2. Coloque un archivo usercustomize.py en ese directorio con el siguiente código:

     import traceback import warnings _old_warn = warnings.warn def warn(*args, **kwargs): tb = traceback.extract_stack() _old_warn(*args, **kwargs) print("".join(traceback.format_list(tb)[:-1])) warnings.warn = warn 

    Créditos a esta respuesta por el código.

Ejecute el código como de costumbre. Mi código de prueba:

 import warnings def f(): warnings.warn("foz") f() 

Antes del procedimiento:

 $ python test_warn.py test_warn.py:4: UserWarning: foz warnings.warn("foz") 

Después:

 $ python test_warn.py /usercustomize.py:6: UserWarning: foz _old_warn(*args, **kwargs) File "test_warn.py", line 6, in  f() File "test_warn.py", line 4, in f warnings.warn("foz") 

Si quiero encontrar la raíz de una advertencia, generalmente promociono Warnings a Exceptions .

En su caso, simplemente puede usar warnings.simplefilter o warnings.filterwarnings .

Por ejemplo:

 import warnings def func(): warnings.warn('abc', UserWarning) def func2(): func() # Here I promote all UserWarnings to exceptions, but you could also use "warnings.filterwarnings" # to only promote warnings from a specified module or matching a specified message. # You may need to check which is most useful/appropriate for you. warnings.simplefilter("error", UserWarning) # you might need to reset this later :) func2() 

lo que le da una traza completa:

 --------------------------------------------------------------------------- UserWarning Traceback (most recent call last)  in () 8 9 warnings.simplefilter("error", UserWarning) ---> 10 func2()  in func2() 5 6 def func2(): ----> 7 func() 8 9 warnings.simplefilter("error", UserWarning)  in func() 2 3 def func(): ----> 4 warnings.warn('abc', UserWarning) 5 6 def func2(): UserWarning: abc 

Y si desea depurar esto, podría enganchar fácilmente Pythons pdb s en la última excepción encontrada:

 import pdb pdb.pm() 

Resultando en:

 > (4)func() -> warnings.warn('abc', UserWarning) (Pdb) _________________ 

Esto inicia un análisis post mortem de la última excepción encontrada. Lo que debería permitirte cavar a través de los marcos e inspeccionar las variables, etc.


También preguntó por una bandera, y de hecho hay una bandera que habilita el “manejo de advertencia”, la bandera -W . Es muy parecido a la función warnings.filterwarnings . Para mayor comodidad, acabo de copiar la documentación de la bandera -W aquí:

Control de advertencia. La maquinaria de advertencia de Python imprime mensajes de advertencia a sys.stderr de forma predeterminada. Un mensaje de advertencia típico tiene la siguiente forma:

 file:line: category: message 

De forma predeterminada, cada advertencia se imprime una vez para cada línea de origen donde se produce. Esta opción controla la frecuencia con la que se imprimen las advertencias.

Se pueden dar múltiples opciones -W; cuando una advertencia coincide con más de una opción, se realiza la acción para la última opción coincidente. Las opciones -W no válidas se ignoran (sin embargo, se imprime un mensaje de advertencia sobre las opciones no válidas cuando se emite la primera advertencia).

A partir de Python 2.7, DeprecationWarning y sus descendientes se ignoran de forma predeterminada. La opción -Wd se puede usar para volver a habilitarlos.

Las advertencias también se pueden controlar desde un progtwig Python mediante el módulo de advertencias.

La forma más simple de argumento es una de las siguientes cadenas de acción (o una abreviatura única) por sí mismas:

  • ignorar

    Ignora todas las advertencias.

  • defecto

    Solicite explícitamente el comportamiento predeterminado (imprimiendo cada advertencia una vez por línea de origen).

  • todos

    Imprima una advertencia cada vez que ocurra (esto puede generar muchos mensajes si una advertencia se activa repetidamente para la misma línea de origen, como dentro de un bucle).

  • módulo

    Imprima cada advertencia solo la primera vez que ocurra en cada módulo.

  • una vez

    Imprima cada advertencia solo la primera vez que ocurra en el progtwig.

  • error:

    Genere una excepción en lugar de imprimir un mensaje de advertencia.

La forma completa de argumento es:

 action:message:category:module:line 

Aquí, la acción es como se explicó anteriormente, pero solo se aplica a los mensajes que coinciden con los campos restantes. Los campos vacíos coinciden con todos los valores; los campos vacíos finales pueden ser omitidos. El campo de mensaje coincide con el inicio del mensaje de advertencia impreso; esta coincidencia no distingue entre mayúsculas y minúsculas El campo de categoría coincide con la categoría de advertencia. Este debe ser un nombre de clase; la coincidencia comprueba si la categoría de advertencia real del mensaje es una subclase de la categoría de advertencia especificada. El nombre completo de la clase debe ser dado. El campo del módulo coincide con el nombre del módulo (completamente calificado); esta coincidencia distingue entre mayúsculas y minúsculas El campo de línea coincide con el número de línea, donde cero coincide con todos los números de línea y, por lo tanto, es equivalente a un número de línea omitido.