Comment enregistrer les valeurs traceback / sys.exc_info () dans une variable?

127

Je souhaite enregistrer le nom de l'erreur et les détails du retraçage dans une variable. Voici ma tentative.

import sys
try:
    try:
        print x
    except Exception, ex:
        raise NameError
except Exception, er:
    print "0", sys.exc_info()[0]
    print "1", sys.exc_info()[1]
    print "2", sys.exc_info()[2]

Production:

0 <type 'exceptions.NameError'>
1 
2 <traceback object at 0xbd5fc8>

Sortie désirée:

0 NameError
1
2 Traceback (most recent call last):
  File "exception.py", line 6, in <module>
    raise NameError

PS Je sais que cela peut être fait facilement en utilisant le module de traceback, mais je veux connaître l'utilisation de l'objet sys.exc_info () [2] ici.

codeurs du noir
la source
Avez-vous essayé d'imprimer sys.exc_info () [x] .__ str __ ()?
zmbq
3
Vous avez peut-être mal compris ce qui se passe dans votre programme: ce que vous appelez "objet sys.exc_info () [2]" est une instance de l'objet traceback (= vous utilisez déjà le module traceback). Maintenant, vous pouvez manipuler cet objet sans utiliser les fonctions d'assistance dans le module de traceback, mais cela ne change pas le fait que vous l'utilisez toujours. :)
mac
1
Alors @mac, aidez-moi à accéder à la valeur de cet objet avec ou sans l'aide de la fonction d'assistance.
codersofthedark
1
@dragosrsupercool - Comme je l'ai mentionné dans ma réponse ci-dessous, vous devriez consulter la documentation de traceback . J'ai fourni un exemple de la façon de récupérer les données textuellement, mais il existe d'autres méthodes de l'objet qui vous permettent d'extraire le nom de l'exception, la ligne du code, etc ... la bonne dépend vraiment de la façon dont vous voulez manipuler le valeur après ...
mac
1
Ma réponse à une autre question peut aider à illustrer les détails - avec des liens! Pour les chaînes prédéfinies, le module de traceback de la bibliothèque standard semble correct. Si vous souhaitez obtenir les détails, lisez la source ( <python install path>/Lib/traceback.py) pour plus d'informations.
pythonlarry

Réponses:

180

Voici comment je fais:

>>> import traceback
>>> try:
...   int('k')
... except:
...   var = traceback.format_exc()
... 
>>> print var
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'k'

Vous devriez cependant jeter un œil à la documentation de traceback , car vous y trouverez peut-être des méthodes plus appropriées, en fonction de la façon dont vous souhaitez traiter votre variable par la suite ...

Mac
la source
1
Je cherchais une méthode sans utiliser le module de traceback. Y a-t-il une façon dont nous pouvons simplement imprimer les détails de trace à partir de cette référence d'objet? sys.exc_info () [2]
codersofthedark
Bon, c'est pourquoi j'ai pensé que nous pouvions faire quelque chose comme sys.exc_info () [2] .format_exc (), mais cela ne fonctionne pas .. Ainsi, je me demande comment puis-je extraire la valeur de l'objet de trace sys.exc_info () [2]. Une idée?
codersofthedark
1
sys.exc_info () [2] .tb_text donne une erreur de suivi -> AttributeError: l'objet 'traceback' n'a pas d'attribut 'tb_text'
codersofthedark
4
@dragosrsupercool - sys.exc_info()[2].tb_frame.f_code.co_names[3], mais cela n'a aucun sens ... S'il y a un module appelé tracebackdans la bibliothèque standard, il y a une raison à cela ... :)
mac
2
@codersofthedark traceback.format_exception(*sys.exc_info())est le moyen de le faire. Mais c'est fonctionnellement équivalent à traceback.format_exc().
wizzwizz4
25

sys.exc_info () retourne un tuple avec trois valeurs (type, valeur, traceback).

  1. Ici, type obtient le type d'exception de l'exception en cours de traitement
  2. la valeur est les arguments qui sont passés au constructeur de la classe d'exception
  3. traceback contient les informations de pile comme l'endroit où l'exception s'est produite, etc.

Par exemple, dans le programme suivant

try:

    a = 1/0

except Exception,e:

    exc_tuple = sys.exc_info()

Maintenant, si nous imprimons le tuple, les valeurs seront les suivantes.

  1. La valeur de exc_tuple [0] sera " ZeroDivisionError "
  2. La valeur exc_tuple [1] sera " division entière ou modulo par zéro " (chaîne passée en paramètre à la classe d'exception)
  3. La valeur de exc_tuple [2] sera " objet de rétrolien à (une adresse mémoire) "

Les détails ci-dessus peuvent également être récupérés en imprimant simplement l'exception au format chaîne.

print str(e)
keya
la source
Pour Python3, exc_tuple [1] (aka value) est l' instance de l'exception, pas la "chaîne passée en paramètre". Voir: docs.python.org/3/library/sys.html#sys.exc_info
Jinghao Shi
Ne devrait-il pas être "sauf exception comme e:"?
Henrik
20

À utiliser traceback.extract_stack()si vous souhaitez un accès pratique aux noms de module et de fonction et aux numéros de ligne.

À utiliser ''.join(traceback.format_stack())si vous voulez juste une chaîne qui ressemble à la traceback.print_stack()sortie.

Notez que même avec ''.join()vous obtiendrez une chaîne multiligne, puisque les éléments de format_stack()contiennent \n. Voir la sortie ci-dessous.

N'oubliez pas import traceback.

Voici la sortie de traceback.extract_stack(). Mise en forme ajoutée pour la lisibilité.

>>> traceback.extract_stack()
[
   ('<string>', 1, '<module>', None),
   ('C:\\Python\\lib\\idlelib\\run.py', 126, 'main', 'ret = method(*args, **kwargs)'),
   ('C:\\Python\\lib\\idlelib\\run.py', 353, 'runcode', 'exec(code, self.locals)'),
   ('<pyshell#1>', 1, '<module>', None)
]

Voici la sortie de ''.join(traceback.format_stack()). Mise en forme ajoutée pour la lisibilité.

>>> ''.join(traceback.format_stack())
'  File "<string>", line 1, in <module>\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 126, in main\n
       ret = method(*args, **kwargs)\n
   File "C:\\Python\\lib\\idlelib\\run.py", line 353, in runcode\n
       exec(code, self.locals)\n  File "<pyshell#2>", line 1, in <module>\n'
Nathan
la source
4

Soyez prudent lorsque vous retirez l'objet d'exception ou l'objet de trace du gestionnaire d'exceptions, car cela provoque des références circulaires et gc.collect()échouera à collecter. Cela semble être un problème particulier dans l'environnement du notebook ipython / jupyter où l'objet de trace n'est pas effacé au bon moment et même un appel explicite à gc.collect()in finallysection ne fait rien. Et c'est un énorme problème si vous avez des objets énormes qui ne récupèrent pas leur mémoire à cause de cela (par exemple, les exceptions de mémoire insuffisante de CUDA qui sans cette solution nécessitent un redémarrage complet du noyau pour récupérer).

En général, si vous souhaitez enregistrer l'objet de trace, vous devez le supprimer des références à locals(), comme ceci:

import sys, traceback, gc
type, val, tb = None, None, None
try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
# some cleanup code
gc.collect()
# and then use the tb:
if tb:
    raise type(val).with_traceback(tb)

Dans le cas du notebook jupyter, vous devez au moins le faire dans le gestionnaire d'exceptions:

try:
    myfunc()
except:
    type, val, tb = sys.exc_info()
    traceback.clear_frames(tb)
    raise type(val).with_traceback(tb)
finally:
    # cleanup code in here
    gc.collect()

Testé avec python 3.7.

ps le problème avec ipython ou jupyter notebook env est qu'il a de la %tbmagie qui enregistre le traçage et le rend disponible à tout moment plus tard. Et par conséquent, toutes locals()les trames participant au suivi ne seront pas libérées jusqu'à ce que le bloc-notes se termine ou qu'une autre exception écrase la trace arrière précédemment stockée. C'est très problématique. Il ne doit pas stocker la trace sans nettoyer ses cadres. Correctif soumis ici .

stason
la source
3

L'objet peut être utilisé comme paramètre en Exception.with_traceback()fonction:

except Exception as e:
    tb = sys.exc_info()
    print(e.with_traceback(tb[2]))
Hansen D'Silva
la source