web-dev-qa-db-fra.com

Récupère la description de l'exception et la trace de la pile qui a provoqué une exception, le tout sous forme de chaîne

J'ai vu beaucoup d'articles sur la trace de la pile et les exceptions en Python. Mais je n'ai pas trouvé ce dont j'avais besoin.

J'ai un morceau de code Python 2.7 pouvant déclencher une exception. Je voudrais attraper et affecter à un chaîne sa description complète et la trace de la pile qui a provoqué l'erreur (simplement tout ce que nous utilisons pour voir sur la console). J'ai besoin de cette chaîne pour l'imprimer dans une zone de texte dans l'interface graphique.

Quelque chose comme ça:

try:
    method_that_can_raise_an_exception(params)
except Exception as e:
    print_to_textbox(complete_exception_description(e))

Le problème est: quelle est la fonction complete_exception_description?

348
bluish

Voir le module traceback, en particulier la fonction format_exc(). ici .

import traceback

try:
    raise ValueError
except ValueError:
    tb = traceback.format_exc()
else:
    tb = "No error"
finally:
    print tb
522
kindall

Créons un stacktrace décemment compliqué, afin de démontrer que nous obtenons le stacktrace complet:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

Journalisation de la pile complète

Une meilleure pratique consiste à configurer un enregistreur pour votre module. Il connaîtra le nom du module et pourra changer de niveau (entre autres attributs, tels que des gestionnaires).

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

Et nous pouvons utiliser cet enregistreur pour obtenir l'erreur:

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

Quels journaux:

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!

Et nous obtenons le même résultat que lorsque nous avons une erreur:

>>> 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!

Obtenir juste la ficelle

Si vous voulez vraiment juste la chaîne, utilisez plutôt la fonction traceback.format_exc, en montrant comment enregistrer la chaîne ici:

import traceback
try:
    do_something_that_might_error()
except Exception as error:
    just_the_string = traceback.format_exc()
    logger.debug(just_the_string)

Quels journaux:

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!
56
Aaron Hall
>>> 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

Vous utilisez sys.exc_info () pour collecter les informations et les fonctions du module traceback afin de le formater. Ici quelques exemples pour le formater.

Toute la chaîne d'exception est à:

>>> 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']
33
aeter

Avec Python 3, le code suivant formate un objet Exception exactement comme s'il était obtenu avec 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__)))

L'avantage est que seul l'objet Exception est nécessaire (grâce à l'attribut __traceback__ enregistré) et qu'il peut donc être plus facilement transmis en tant qu'argument à une autre fonction pour un traitement ultérieur.

31
Erwin Mayer

Pour ceux qui utilisent Python-

En utilisant traceback module et exception.__traceback__, on peut extraire la trace de pile comme suit:

  • récupérez le actuel pile-trace en utilisant traceback.extract_stack()
  • supprime les trois derniers éléments (car ce sont des entrées dans la pile qui m'ont amené à ma fonction de débogage)
  • ajoute le __traceback__ de l'objet exception à l'aide de traceback.extract_tb()
  • formatez le tout en utilisant 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)

Une démonstration simple:

def foo():
    try:
        something_invalid()
    except Exception as e:
        print(exception_to_string(e))

def bar():
    return foo()

Nous obtenons la sortie suivante lorsque nous appelons 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
7
Mike N

Vous pouvez également envisager d’utiliser le module intégré Python, cgitb , pour obtenir de très bons , informations d’exception joliment formatées, y compris les valeurs des variables locales, le contexte du code source, les paramètres de fonction, etc.

Par exemple pour ce code ...

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)

nous obtenons cette sortie d'exception ...

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
5
samaspin

Si vous souhaitez obtenir les mêmes informations lorsqu'une exception n'est pas gérée, vous pouvez procéder de la sorte. Faites import traceback puis:

try:
    ...
except Exception as e:
    print(traceback.print_tb(e.__traceback__))

J'utilise Python 3.7.

3
SamuelN

mes 2 centimes:

import sys, traceback
try: 
  ...
except Exception, e:
  T, V, TB = sys.exc_info()
  print ''.join(traceback.format_exception(T,V,TB))
1
user 1155692

J'ai défini la classe d'assistance suivante:

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 je peux utiliser plus tard comme ceci:

with TracedExeptions():
  #some-code-which-might-throw-any-exception

Et plus tard peut le consommer comme ça:

def log_err(ex):
  if hasattr(ex, 'traceString'):
    print("ERROR:{}".format(ex.traceString));
  else:
    print("ERROR:{}".format(ex));

(Background: j'ai été frustré à cause de l'utilisation de Promises avec Exceptions, qui transmet malheureusement les exceptions levées à un endroit à un gestionnaire on_rejected situé à un autre endroit. Il est donc difficile d'obtenir le suivi depuis l'emplacement d'origine. )

0
qbolec