Ottenere Descrizione eccezione e stack trace che ha causato un'eccezione, il tutto come una stringa
-
14-10-2019 - |
Domanda
Ho visto un sacco di post su stack trace e le eccezioni in Python. Ma non ho trovato quello che mi serve.
Ho un pezzo di codice Python 2.7 che può sollevare un'eccezione. Vorrei prenderlo e assegnato a un stringa la sua descrizione completa e l'analisi dello stack che ha causato l'errore (semplicemente tutto quello che usiamo per vedere sulla console). Ho bisogno di questo stringa per stamparlo su una casella di testo nella GUI.
Qualcosa di simile a questo:
try:
method_that_can_raise_an_exception(params)
except Exception as e:
print_to_textbox(complete_exception_description(e))
Il problema è:? qual è la funzione complete_exception_description
Soluzione
Si veda il modulo traceback
, in particolare la funzione format_exc()
. Qui .
import traceback
try:
raise ValueError
except ValueError:
tb = traceback.format_exc()
else:
tb = "No error"
finally:
print tb
Altri suggerimenti
Creiamo uno stacktrace decentemente complicato, al fine di dimostrare che si ottiene il pieno stacktrace:
def raise_error():
raise RuntimeError('something bad happened!')
def do_something_that_might_error():
raise_error()
Logging la piena stacktrace
Una pratica migliore è quella di avere un logger configurato per il modulo. Si conosce il nome del modulo ed essere in grado di modificare i livelli di (tra gli altri attributi, come ad esempio i gestori)
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
E possiamo usare questo logger per ottenere l'errore:
try:
do_something_that_might_error()
except Exception as error:
logger.exception(error)
che registra:
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!
E così otteniamo lo stesso risultato quando abbiamo un errore:
>>> 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!
Come solo la stringa
Se si vuole veramente solo la stringa, utilizzare la funzione traceback.format_exc
invece, dimostrando la registrazione la stringa qui:
import traceback
try:
do_something_that_might_error()
except Exception as error:
just_the_string = traceback.format_exc()
logger.debug(just_the_string)
che registra:
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
Si utilizza sys.exc_info () per raccogliere le informazioni e le funzioni del modulo traceback
di formattarlo.
qui sono alcuni esempi di formattazione.
La stringa eccezione intero e ':
>>> 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, il seguente codice formatta un oggetto Exception
esattamente quella che si otterrebbe con 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__)))
Il vantaggio è che solo l'oggetto Exception
è necessaria (grazie all'attributo __traceback__
registrato), e può quindi essere più facilmente passato come argomento a un'altra funzione per l'ulteriore elaborazione.
Per coloro che utilizzano Python-3
Utilizzando il modulo traceback
e uno exception.__traceback__
in grado di estrarre la pila-traccia come segue:
- afferrare il corrente dello stack-trace utilizzando
traceback.extract_stack()
- rimuovere gli ultimi tre elementi (come quelle sono voci nella stack che mi ha fatto la mia funzione di debug)
- Aggiungere il
__traceback__
dall'oggetto eccezione utilizzandotraceback.extract_tb()
- formattare il tutto utilizzando
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 semplice dimostrazione:
def foo():
try:
something_invalid()
except Exception as e:
print(exception_to_string(e))
def bar():
return foo()
Si ottiene il seguente output quando chiamiamo 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
Si potrebbe anche considerare l'uso del modulo incorporato Python, cgitb , per ottenere alcuni davvero buona, informazioni eccezione ben formattata compresi i valori delle variabili locali, nell'ambito del codice sorgente, i parametri di funzione, ecc ..
Per esempio per questo codice ...
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)
otteniamo questo output eccezione ...
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
Se si desidera ottenere le stesse informazioni dato quando un'eccezione non viene gestita si può fare qualcosa di simile. Fare import traceback
e poi:
try:
...
except Exception as e:
print(traceback.print_tb(e.__traceback__))
Sto usando Python 3.7.
i miei 2 centesimi:
import sys, traceback
try:
...
except Exception, e:
T, V, TB = sys.exc_info()
print ''.join(traceback.format_exception(T,V,TB))
Ho definito seguente classe di supporto:
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
Il che posso usare più avanti in questo modo:
with TracedExeptions():
#some-code-which-might-throw-any-exception
E più tardi può consumare in questo modo:
def log_err(ex):
if hasattr(ex, 'traceString'):
print("ERROR:{}".format(ex.traceString));
else:
print("ERROR:{}".format(ex));
(Background: stavo frustraded a causa dell'utilizzo di Promise
s insieme Exception
s, che passa purtroppo eccezioni sollevate in un posto per un gestore on_rejected in un altro luogo, e quindi è difficile ottenere il traceback dalla posizione originale)