Obtenga una descripción de excepción y traza de pila que causó una excepción, todo como cadena
-
14-10-2019 - |
Pregunta
He visto muchas publicaciones sobre Stack Trace y excepciones en Python. Pero no he encontrado lo que necesito.
Tengo una parte del código Python 2.7 que puede plantear una excepción. Me gustaría atraparlo y asignar a un cuerda Su descripción completa y la traza de pila que causó el error (simplemente todo lo que usamos para ver en la consola). Necesito esta cadena para imprimirla en un cuadro de texto en la GUI.
Algo como esto:
try:
method_that_can_raise_an_exception(params)
except Exception as e:
print_to_textbox(complete_exception_description(e))
El problema es: Cuál es la función complete_exception_description
?
Solución
Ver el traceback
módulo, específicamente el format_exc()
función. Aquí.
import traceback
try:
raise ValueError
except ValueError:
tb = traceback.format_exc()
else:
tb = "No error"
finally:
print tb
Otros consejos
Creemos una StackTrace decentemente complicada, para demostrar que obtenemos el StackTrace completo:
def raise_error():
raise RuntimeError('something bad happened!')
def do_something_that_might_error():
raise_error()
Registro de la Stacktrace completa
Una mejor práctica es tener un registrador configurado para su módulo. Conocerá el nombre del módulo y podrá cambiar los niveles (entre otros atributos, como los manejadores)
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
Y podemos usar este registrador para obtener el error:
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!
Y entonces obtenemos la misma salida 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!
Conseguir solo la cadena
Si realmente solo quieres la cadena, usa la traceback.format_exc
Funciona en su lugar, demostrando registrar la cadena aquí:
import traceback
try:
do_something_that_might_error()
except Exception as error:
just_the_string = traceback.format_exc()
logger.debug(just_the_string)
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!
>>> import sys
>>> import traceback
>>> try:
... 5 / 0
... except ZeroDivisionError as e:
... type_, value_, traceback_ = sys.exc_info()
>>> traceback.format_tb(traceback_)
[' File "<stdin>", line 2, in <module>\n']
>>> value_
ZeroDivisionError('integer division or modulo by zero',)
>>> type_
<type 'exceptions.ZeroDivisionError'>
>>>
>>> 5 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
Tu usas sys.exc_info () para recopilar la información y las funciones en el traceback
módulo para formatearlo.Aquí son algunos ejemplos para formatearlo.
Toda la cadena de excepción está en:
>>> ex = traceback.format_exception(type_, value_, traceback_)
>>> ex
['Traceback (most recent call last):\n', ' File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: integer division or modulo by zero\n']
Con Python 3, el siguiente código formateará un Exception
objeto exactamente como se obtendría usando traceback.format_exc()
:
import traceback
try:
method_that_can_raise_an_exception(params)
except Exception as ex:
print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))
La ventaja es que solo el Exception
se necesita objeto (gracias a los grabados __traceback__
atributo) y, por lo tanto, se puede pasar más fácilmente como argumento a otra función para el procesamiento posterior.
Para aquellos que usan Python-3
Usando traceback
módulo y exception.__traceback__
Uno puede extraer el ritmo de pila de la siguiente manera:
- toma el Actual pilotear
traceback.extract_stack()
- Elimine los últimos tres elementos (ya que esas son entradas en la pila que me llevaron a mi función de depuración)
- Agregar el
__traceback__
Desde el objeto de excepción usandotraceback.extract_tb()
- formatear todo usando
traceback.format_list()
import traceback
def exception_to_string(excp):
stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__) # add limit=??
pretty = traceback.format_list(stack)
return ''.join(pretty) + '\n {} {}'.format(excp.__class__,excp)
Una simple demostración:
def foo():
try:
something_invalid()
except Exception as e:
print(exception_to_string(e))
def bar():
return foo()
Obtenemos la siguiente salida cuando llamamos bar()
:
File "./test.py", line 57, in <module>
bar()
File "./test.py", line 55, in bar
return foo()
File "./test.py", line 50, in foo
something_invalid()
<class 'NameError'> name 'something_invalid' is not defined
También puede considerar usar el módulo Python incorporado, CGITB, para obtener información de excepción realmente buena y bien formateada que incluye valores variables locales, contexto del código fuente, parámetros de función, etc.
Por ejemplo, para este código ...
import cgitb
cgitb.enable(format='text')
def func2(a, divisor):
return a / divisor
def func1(a, b):
c = b - 5
return func2(a, c)
func1(1, 5)
obtenemos esta salida de excepción ...
ZeroDivisionError
Python 3.4.2: C:\tools\python\python.exe
Tue Sep 22 15:29:33 2015
A problem occurred in a Python script. Here is the sequence of
function calls leading up to the error, in the order they occurred.
c:\TEMP\cgittest2.py in <module>()
7 def func1(a, b):
8 c = b - 5
9 return func2(a, c)
10
11 func1(1, 5)
func1 = <function func1>
c:\TEMP\cgittest2.py in func1(a=1, b=5)
7 def func1(a, b):
8 c = b - 5
9 return func2(a, c)
10
11 func1(1, 5)
global func2 = <function func2>
a = 1
c = 0
c:\TEMP\cgittest2.py in func2(a=1, divisor=0)
3
4 def func2(a, divisor):
5 return a / divisor
6
7 def func1(a, b):
a = 1
divisor = 0
ZeroDivisionError: division by zero
__cause__ = None
__class__ = <class 'ZeroDivisionError'>
__context__ = None
__delattr__ = <method-wrapper '__delattr__' of ZeroDivisionError object>
__dict__ = {}
__dir__ = <built-in method __dir__ of ZeroDivisionError object>
__doc__ = 'Second argument to a division or modulo operation was zero.'
__eq__ = <method-wrapper '__eq__' of ZeroDivisionError object>
__format__ = <built-in method __format__ of ZeroDivisionError object>
__ge__ = <method-wrapper '__ge__' of ZeroDivisionError object>
__getattribute__ = <method-wrapper '__getattribute__' of ZeroDivisionError object>
__gt__ = <method-wrapper '__gt__' of ZeroDivisionError object>
__hash__ = <method-wrapper '__hash__' of ZeroDivisionError object>
__init__ = <method-wrapper '__init__' of ZeroDivisionError object>
__le__ = <method-wrapper '__le__' of ZeroDivisionError object>
__lt__ = <method-wrapper '__lt__' of ZeroDivisionError object>
__ne__ = <method-wrapper '__ne__' of ZeroDivisionError object>
__new__ = <built-in method __new__ of type object>
__reduce__ = <built-in method __reduce__ of ZeroDivisionError object>
__reduce_ex__ = <built-in method __reduce_ex__ of ZeroDivisionError object>
__repr__ = <method-wrapper '__repr__' of ZeroDivisionError object>
__setattr__ = <method-wrapper '__setattr__' of ZeroDivisionError object>
__setstate__ = <built-in method __setstate__ of ZeroDivisionError object>
__sizeof__ = <built-in method __sizeof__ of ZeroDivisionError object>
__str__ = <method-wrapper '__str__' of ZeroDivisionError object>
__subclasshook__ = <built-in method __subclasshook__ of type object>
__suppress_context__ = False
__traceback__ = <traceback object>
args = ('division by zero',)
with_traceback = <built-in method with_traceback of ZeroDivisionError object>
The above is a description of an error in a Python program. Here is
the original traceback:
Traceback (most recent call last):
File "cgittest2.py", line 11, in <module>
func1(1, 5)
File "cgittest2.py", line 9, in func1
return func2(a, c)
File "cgittest2.py", line 5, in func2
return a / divisor
ZeroDivisionError: division by zero
Si desea obtener la misma información dada cuando no se maneja una excepción, puede hacer algo como esto. Hacer import traceback
y entonces:
try:
...
except Exception as e:
print(traceback.print_tb(e.__traceback__))
Estoy usando Python 3.7.
mis 2 centavos:
import sys, traceback
try:
...
except Exception, e:
T, V, TB = sys.exc_info()
print ''.join(traceback.format_exception(T,V,TB))
Definí la siguiente clase de ayudante:
import traceback
class TracedExeptions(object):
def __init__(self):
pass
def __enter__(self):
pass
def __exit__(self, etype, value, tb):
if value :
if not hasattr(value, 'traceString'):
value.traceString = "\n".join(traceback.format_exception(etype, value, tb))
return False
return True
Que luego puedo usar así:
with TracedExeptions():
#some-code-which-might-throw-any-exception
Y luego puede consumirlo así:
def log_err(ex):
if hasattr(ex, 'traceString'):
print("ERROR:{}".format(ex.traceString));
else:
print("ERROR:{}".format(ex));
(Antecedentes: estaba frustrado por usar Promise
s junto con Exception
S, que desafortunadamente pasa excepciones planteadas en un lugar a un controlador de On_rejected en otro lugar, y por lo tanto es difícil obtener el rastreo de la ubicación original)