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

È stato utile?

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 utilizzando traceback.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 Promises insieme Exceptions, 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)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top