169 votes

Extraire les informations de traçage d'un objet d'exception

Étant donné un objet d'exception (d'origine inconnue), existe-t-il un moyen d'obtenir sa trace ? J'ai un code comme celui-ci :

def stuff():
   try:
       .....
       return useful
   except Exception as e:
       return e

result = stuff()
if isinstance(result, Exception):
    result.traceback <-- How?

Comment puis-je extraire le retour de trace de l'objet Exception une fois que je l'ai obtenu ?

125voto

senderle Points 41607

La réponse à cette question dépend de la version de Python que vous utilisez.

En Python 3

C'est simple : les exceptions sont dotées d'une __traceback__ qui contient le retour de trace. Cet attribut est également accessible en écriture, et peut être défini de manière pratique à l'aide de la commande with_traceback méthode des exceptions :

raise Exception("foo occurred").with_traceback(tracebackobj)

Ces caractéristiques sont décrites de façon minimale dans le cadre de la raise documentation.

Tout le mérite pour cette partie de la réponse revient à Vyctor, qui a d'abord publié cette information . Je l'inclus ici uniquement parce que cette réponse est bloquée au sommet, et que Python 3 devient plus courant.

En Python 2

C'est ennuyeusement complexe. Le problème avec les tracebacks, c'est qu'ils ont des références aux frames de la pile, et les frames de la pile ont des références aux tracebacks qui ont des références aux frames de la pile qui ont des références à... vous voyez le genre. Cela pose des problèmes au ramasseur d'ordures. (Merci à ecatmur pour avoir été le premier à le signaler).

La bonne façon de résoudre ce problème serait d'opérer chirurgicalement briser le cycle après avoir quitté le except ce que fait Python 3. La solution de Python 2 est beaucoup plus laide : on vous fournit une fonction ad-hoc, sys.exc_info() qui ne fonctionne qu'à l'intérieur de la except clause . Il renvoie un tuple contenant l'exception, le type d'exception et la trace de l'exception en cours de traitement.

Donc si vous êtes à l'intérieur du except vous pouvez utiliser la sortie de la clause sys.exc_info() ainsi que le traceback pour faire diverses choses utiles :

>>> import sys, traceback
>>> def raise_exception():
...     try:
...         raise Exception
...     except Exception:
...         ex_type, ex, tb = sys.exc_info()
...         traceback.print_tb(tb)
...     finally:
...         del tb
... 
>>> raise_exception()
  File "<stdin>", line 3, in raise_exception

Mais comme votre édition l'indique, vous essayez d'obtenir le retour de trace qui serait ont été imprimés si votre exception n'avait pas été traitée, après qu'elle ait été déjà ont été traitées. C'est une question beaucoup plus difficile. Malheureusement, sys.exc_info renvoie à (None, None, None) lorsqu'aucune exception n'est traitée. Autres éléments liés sys Les attributs n'aident pas non plus. sys.exc_traceback est déprécié et non défini lorsqu'aucune exception n'est gérée ; sys.last_traceback semble parfaite, mais elle ne semble être définie que lors des sessions interactives.

Si vous pouvez contrôler la façon dont l'exception est levée, vous pouvez utiliser la fonction inspect et un exception personnalisée pour stocker certaines des informations. Mais je ne suis pas tout à fait sûr de la façon dont cela fonctionnerait.

À vrai dire, attraper et renvoyer une exception est une chose plutôt inhabituelle à faire. C'est peut-être le signe que vous devez quand même procéder à un remaniement.

104voto

Vyktor Points 10531

Desde Python 3.0 [PEP 3109] la classe intégrée Exception a un __traceback__ qui contient un traceback object (avec Python 3.2.3) :

>>> try:
...     raise Exception()
... except Exception as e:
...     tb = e.__traceback__
...
>>> tb
<traceback object at 0x00000000022A9208>

Le problème est qu'après Sur Google : __traceback__ pendant un certain temps, je n'ai trouvé que quelques articles mais aucun d'entre eux ne décrit si ou pourquoi vous devriez (ne pas) utiliser __traceback__ .

Cependant, le Documentation de Python 3 pour raise dit cela :

Un objet traceback est normalement créé automatiquement lorsqu'une exception est levée et attaché à celle-ci en tant qu'objet de type __traceback__ qui est accessible en écriture.

Donc je suppose que c'est fait pour être utilisé.

82voto

Hieu Points 473

Une façon d'obtenir un retour de trace sous forme de chaîne de caractères à partir d'un objet d'exception en Python 3 :

import traceback

# `e` is an exception object that you get from somewhere
traceback_str = ''.join(traceback.format_tb(e.__traceback__))

traceback.format_tb(...) renvoie une liste de chaînes de caractères. ''.join(...) les réunit. Pour plus de références, veuillez consulter le site : https://docs.python.org/3/library/traceback.html#traceback.format_tb

46voto

Daniel Porteous Points 1029

Par ailleurs, si vous voulez obtenir la trace complète telle qu'elle serait imprimée dans votre terminal, il vous faut ceci :

>>> try:
...     print(1/0)
... except Exception as e:
...     exc = e
...
>>> exc
ZeroDivisionError('division by zero')
>>> tb_str = traceback.format_exception(etype=type(exc), value=exc, tb=exc.__traceback__)
>>> tb_str
['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: division by zero\n']
>>> print("".join(tb_str))
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

Si vous utilisez format_tb comme le suggèrent les réponses ci-dessus, vous obtiendrez moins d'informations :

>>> tb_str = "".join(traceback.format_tb(exc.__traceback__))
>>> print("".join(tb_str))
  File "<stdin>", line 2, in <module>

10voto

Mark Points 186

Vous pourriez utiliser traceback.format_exc qui renvoie un str

o traceback.print_exc imprime dans la sortie d'imprimante

import traceback

try:
    b"x81".decode()
except UnicodeError:
    traceback.print_exc() # prints to stdout
    my_traceback = traceback.format_exc() # returns a str
print(my_traceback)

Si usted besoin de pour l'obtenir à partir de l'exception réelle (bien que je ne vois pas pourquoi)

traceback.format_exception renvoie un str

traceback.print_exception imprime dans la sortie d'imprimante

import traceback

try:
    b"x81".decode()
except UnicodeError as exc:
    # etype is inferred from `value` since python3.5 so no need to pass a value...
    # format_exception returns a list
    my_traceback = "".join(traceback.format_exception(etype=None, value=exc, tb=exc.__traceback__))
    traceback.print_exception(etype=None, value=exc, tb=exc.__traceback__)

Attention

Ne pas stocker une référence à __traceback__ (ou exc ) pour une utilisation ultérieure car l'objet traceback contient des références à tous les objets stack frame, qui constituent la pile d'appels, et chaque stack frame contient des références à toutes ses variables locales. Ainsi, la taille de la fermeture transitive des objets accessibles depuis l'objet traceback peut être très grande. Et si vous maintenez cette référence, ces objets ne seront pas ramassés à la poubelle. Il est préférable de rendre les tracebacks sous une autre forme pour un stockage en mémoire, même à court terme."

Robert Smallshire - Python Beyond the Basics - 11 - Exceptions et erreurs - Objets Traceback

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X