python - pass - ¿Cómo imprimir el rastreo completo sin detener el programa?
try catch python example (9)
Estoy escribiendo un programa que analiza 10 sitios web, localiza archivos de datos, guarda los archivos y luego los analiza para crear datos que puedan usarse fácilmente en la biblioteca NumPy. Hay toneladas de errores que este archivo encuentra a través de enlaces defectuosos, XML mal formado, entradas faltantes y otras cosas que aún no he categorizado. Inicialmente hice este programa para manejar errores como este:
try:
do_stuff()
except:
pass
Pero ahora quiero registrar errores:
try:
do_stuff()
except Exception, err:
print Exception, err
Tenga en cuenta que esto se está imprimiendo en un archivo de registro para su posterior revisión. Esto suele imprimir datos muy inútiles. Lo que quiero es imprimir exactamente las mismas líneas impresas cuando el error se dispara sin el intento, excepto interceptar la excepción, pero no quiero que detenga mi programa ya que está anidado en una serie de bucles for que me gustaría Ver hasta su finalización.
¿Cómo imprimir el rastreo completo sin detener el programa?
Cuando no quiere detener su programa por un error, necesita manejar ese error con un intento / excepto:
try:
do_something_that_might_error()
except Exception as error:
handle_the_error(error)
Para extraer el rastreo completo, usaremos el módulo de traceback
de la biblioteca estándar:
import traceback
Y para crear un stacktrace decentemente complicado para demostrar que obtenemos el stacktrace completo:
def raise_error():
raise RuntimeError(''something bad happened!'')
def do_something_that_might_error():
raise_error()
Impresión
Para imprimir el rastreo completo, use el método traceback.print_exc
:
try:
do_something_that_might_error()
except Exception as error:
traceback.print_exc()
Que imprime:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Mejor que imprimir, registrar:
Sin embargo, una práctica recomendada es tener un registrador configurado para su módulo. Sabrá el nombre del módulo y podrá cambiar los niveles (entre otros atributos, como los controladores)
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
En cuyo caso, querrá la función logger.exception
lugar:
try:
do_something_that_might_error()
except Exception as error:
logger.exception(error)
Qué registros:
ERROR:__main__:something bad happened!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
O tal vez solo quieras la cadena, en cuyo caso querrás la función traceback.format_exc
:
try:
do_something_that_might_error()
except Exception as error:
logger.debug(traceback.format_exc())
Qué registros:
DEBUG:__main__:Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Conclusión
Y para las tres opciones, vemos que obtenemos el mismo resultado que cuando tenemos un error:
>>> do_something_that_might_error()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Además de la respuesta de @Aaron Hall, si está registrando, pero no quiere usar logging.exception()
(ya que se registra en el nivel ERROR), puede usar un nivel inferior y pasar exc_info=True
. p.ej
try:
do_something_that_might_error()
except Exception:
logger.info(''General exception noted.'', exc_info=True)
Alguna otra respuesta ya ha señalado el módulo de traceback .
Tenga en cuenta que con print_exc
, en algunos casos de esquina, no obtendrá lo que espera. En Python 2.x:
import traceback
try:
raise TypeError("Oups!")
except Exception, err:
try:
raise TypeError("Again !?!")
except:
pass
traceback.print_exc()
... se mostrará el rastreo de la última excepción:
Traceback (most recent call last):
File "e.py", line 7, in <module>
raise TypeError("Again !?!")
TypeError: Again !?!
Si realmente necesita acceder a la solución original de traceback, una es almacenar en caché las informaciones de excepción devueltas por exc_info
en una variable local y mostrarlas con print_exception
:
import traceback
import sys
try:
raise TypeError("Oups!")
except Exception, err:
try:
exc_info = sys.exc_info()
# do you usefull stuff here
# (potentially raising an exception)
try:
raise TypeError("Again !?!")
except:
pass
# end of useful stuff
finally:
# Display the *original* exception
traceback.print_exception(*exc_info)
del exc_info
Productor:
Traceback (most recent call last):
File "t.py", line 6, in <module>
raise TypeError("Oups!")
TypeError: Oups!
Pocos escollos con esto sin embargo:
Desde el documento de exc_info :
Asignar el valor de retorno de rastreo a una variable local en una función que está manejando una excepción causará una referencia circular . Esto evitará que cualquier cosa referenciada por una variable local en la misma función o por el rastreo sea recolectada como basura. [...] Si necesitas el rastreo, asegúrate de eliminarlo después de usarlo (lo mejor es hacerlo con una declaración de prueba ... finalmente)
pero, desde el mismo documento:
A partir de Python 2.2, dichos ciclos se reclaman automáticamente cuando la recolección de basura está habilitada y se vuelven inalcanzables, pero sigue siendo más eficiente para evitar la creación de ciclos.
Por otro lado, al permitirle acceder al rastreo asociado con una excepción, Python 3 produce un resultado menos sorprendente:
import traceback
try:
raise TypeError("Oups!")
except Exception as err:
try:
raise TypeError("Again !?!")
except:
pass
traceback.print_tb(err.__traceback__)
... mostrará:
File "e3.py", line 4, in <module>
raise TypeError("Oups!")
Deberá poner el try / except dentro del bucle más interno donde puede ocurrir el error, es decir
for i in something:
for j in somethingelse:
for k in whatever:
try:
something_complex(i, j, k)
except Exception, e:
print e
try:
something_less_complex(i, j)
except Exception, e:
print e
... y así
En otras palabras, deberá ajustar las declaraciones que pueden fallar en el intento / excepto lo más específico posible, en el bucle más interno posible.
Para obtener la traza precisa de la pila, como una cadena, que se hubiera elevado si no hubiera habido un intento / excepto que hubiera un paso sobre ella, simplemente coloque esto en el bloque de excepción que atrapa la excepción ofensiva.
desired_trace = traceback.format_exc(sys.exc_info())
A continuación, le indicamos cómo usarlo (asumiendo que flaky_func
está definido y el log
llama a su sistema de registro favorito):
import traceback
import sys
try:
flaky_func()
except KeyboardInterrupt:
raise
except Exception:
desired_trace = traceback.format_exc(sys.exc_info())
log(desired_trace)
Es una buena idea capturar y volver a subir KeyboardInterrupt
s, de modo que aún pueda eliminar el programa utilizando Ctrl-C. El registro está fuera del alcance de la pregunta, pero una buena opción es el logging . Documentación para los módulos sys y traceback .
Quieres el módulo de traceback . Te permitirá imprimir volcados de pila como lo hace normalmente Python. En particular, la función print_last imprimirá la última excepción y un seguimiento de pila.
Si está depurando y solo desea ver el seguimiento de pila actual, simplemente puede llamar:
No hay necesidad de hacer manualmente una excepción para volver a atraparla.
Un comentario sobre los comentarios de share : print(traceback.format_exc())
hace un mejor trabajo para mí que traceback.print_exc()
. Con este último, el hello
veces se "mezcla" de forma extraña con el texto de rastreo, como si ambos quisieran escribir en stdout o stderr al mismo tiempo, produciendo una salida extraña (al menos cuando se construye desde un editor de texto y se ve la salida en el panel "Crear resultados").
Rastreo (llamadas recientes más última):
Archivo "C: / Users / User / Desktop / test.py", línea 7, en
hell do_stuff ()
Archivo "C: / Users / User / Desktop / test.py", línea 4, en do_stuff
1/0
ZeroDivisionError: división entera o módulo por cero
o
[Acabado en 0.1s]
Así que uso:
import traceback, sys
def do_stuff():
1/0
try:
do_stuff()
except Exception:
print(traceback.format_exc())
print(''hello'')
traceback.format_exc()
o sys.exc_info()
proporcionará más información si eso es lo que quieres.
import traceback
import sys
try:
do_stuff()
except Exception:
print(traceback.format_exc())
# or
print(sys.exc_info()[0])