La réponse à cette question dépend de la version de Python que vous utilisez.
Dans Python 3
C'est simple: les exceptions sont équipées d'un __traceback__
attribut qui contient le traçage. Cet attribut est également accessible en écriture et peut être facilement défini à l'aide de la with_traceback
méthode des exceptions:
raise Exception("foo occurred").with_traceback(tracebackobj)
Ces fonctionnalités sont décrites de manière minimale dans le cadre de la raise
documentation.
Tout le mérite de cette partie de la réponse doit revenir à Vyctor, qui a publié cette information en premier . Je l'inclus ici uniquement parce que cette réponse est bloquée en haut et que Python 3 est de plus en plus courant.
Dans Python 2
C'est extrêmement complexe. Le problème avec les retraits est qu'ils ont des références aux cadres de pile, et les cadres de pile ont des références aux retraçages qui ont des références aux cadres de pile qui ont des références à ... vous voyez l'idée. Cela provoque des problèmes pour le garbage collector. (Merci à ecatmur d' avoir d'abord signalé cela.)
La bonne façon de résoudre ce problème serait de rompre chirurgicalement le cycle après avoir quitté la except
clause, ce que fait Python 3. La solution Python 2 est beaucoup plus moche: vous disposez d'une fonction ad-hoc sys.exc_info()
, qui ne fonctionne qu'à l'intérieur de la except
clause . Il retourne un tuple contenant l'exception, le type d'exception et la trace de toute exception actuellement gérée.
Donc, si vous êtes à l'intérieur de la except
clause, vous pouvez utiliser la sortie de sys.exc_info()
avec le traceback
module 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 modification l'indique, vous essayez d'obtenir le traçage qui aurait été imprimé si votre exception n'avait pas été gérée, après l'avoir déjà gérée. C'est une question beaucoup plus difficile. Malheureusement, sys.exc_info
retourne (None, None, None)
lorsqu'aucune exception n'est gérée. D'autres sys
attributs connexes n'aident pas non plus. sys.exc_traceback
est obsolète et indéfinie lorsqu'aucune exception n'est gérée; sys.last_traceback
semble parfait, mais il ne semble être défini que lors de sessions interactives.
Si vous pouvez contrôler la manière dont l'exception est déclenchée, vous pourrez peut-être utiliser inspect
une exception personnalisée pour stocker certaines des informations. Mais je ne suis pas tout à fait sûr de savoir comment cela fonctionnerait.
Pour dire la vérité, attraper et renvoyer une exception est quelque chose d'inhabituel à faire. Cela pourrait être un signe que vous devez de toute façon refactoriser.
sys.exc_info
en conjonction avec l' approche de rappel que je suggère sur votre autre question.Depuis Python 3.0 [PEP 3109], la classe intégrée
Exception
a un__traceback__
attribut qui contient untraceback object
(avec Python 3.2.3):Le problème est qu'après avoir cherché
__traceback__
sur Google 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, la documentation Python 3 pour
raise
dit que:Je suppose donc qu'il est destiné à être utilisé.
la source
__
dans le nom indiquant que c'est un détail d'implémentation, pas une propriété publique?__foo
est une méthode privée mais__foo__
(avec des traits de soulignement à la fin aussi) est une méthode "magique" (et non privée).__traceback__
attribut est 100% sûr à utiliser comme vous le souhaitez, sans implication GC. Il est difficile de dire cela à partir de la documentation, mais ecatmur a trouvé des preuves tangibles .Un moyen d'obtenir le traçage sous forme de chaîne à partir d'un objet d'exception dans Python 3:
traceback.format_tb(...)
renvoie une liste de chaînes.''.join(...)
les réunit. Pour plus d'informations, veuillez visiter: https://docs.python.org/3/library/traceback.html#traceback.format_tbla source
En passant, si vous voulez réellement obtenir le traçage complet tel que vous le verriez imprimé sur votre terminal, vous voulez ceci:
Si vous utilisez
format_tb
les réponses ci-dessus, vous obtiendrez moins d'informations:la source
etype=type(exc)
peut être omis maintenant btw: "Modifié dans la version 3.5: l'argument etype est ignoré et déduit du type de valeur." docs.python.org/3.7/library/... Testé en Python 3.7.3.Il y a une très bonne raison pour laquelle le retraçage n'est pas stocké dans l'exception; car le retraçage contient des références aux locaux de sa pile, cela entraînerait une référence circulaire et une fuite de mémoire (temporaire) jusqu'à ce que le GC circulaire entre en jeu. (C'est pourquoi vous ne devriez jamais stocker le retraçage dans une variable locale .)
La seule chose à laquelle je puisse penser serait que vous mesuriez
stuff
les globaux de monkeypatch afin que, quand il pense qu'il attrape,Exception
il attrape en fait un type spécialisé et l'exception se propage à vous en tant qu'appelant:la source
e.__traceback__
.except
bloc, conformément à PEP 3110 .