Comment imprimer le traceback complet sans interrompre le programme?

j'écris un programme qui analyse 10 sites Web, localise des fichiers de données, sauve les fichiers, puis les analyse pour en faire des données qui peuvent être facilement utilisées dans la bibliothèque NumPy. Il y a tonnes d'erreurs que ce fichier rencontre par de mauvais liens, XML mal formé, entrées manquantes, et d'autres choses que je n'ai pas encore à catégoriser. J'ai d'abord fait ce programme pour gérer des erreurs comme celle-ci:

try:
    do_stuff()
except:
    pass

mais maintenant je veux enregistrer les erreurs:

try:
    do_stuff()
except Exception, err:
    print Exception, err

Note Ceci est une impression dans un fichier journal pour une révision ultérieure. Cela imprime habituellement des données très inutiles. Ce que je veux, c'est Imprimer exactement les mêmes lignes imprimées quand l'erreur se déclenche sans essayer-sauf intercepter l'exception, mais je ne veux pas qu'il arrête mon programme car il est imbriqué dans une série de boucles for que je voudrais voir à l'achèvement.

515
demandé sur ᴡʜᴀᴄᴋᴀᴍᴀᴅᴏᴏᴅʟᴇ3000 2010-09-13 21:03:30

8 réponses

une autre réponse a déjà souligné le module traceback .

s'il vous Plaît noter qu'avec print_exc , dans quelque coin des cas, vous n'obtiendrez pas ce que vous attendez. En Python 2.x:

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()

...affichera le traceback du dernier exception:

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!

si vous avez vraiment besoin d'accéder à l'original traceback une solution consiste à mettre en cache les informations d'exception telles que retournées de exc_info dans une variable locale et de l'afficher en utilisant print_exception :

import traceback
import sys

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        exc_info = sys.exc_info()

        # do you usefull stuff here
        # (potentially raising an exception)
        try:
            raise TypeError("Again !?!")
        except:
            pass
        # end of useful stuff


    finally:
        # Display the *original* exception
        traceback.print_exception(*exc_info)
        del exc_info

la Production:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!

quelques pièges avec ceci cependant:

  • De la doc de sys_info :

    L'attribution de la valeur de retour traceback à une variable locale dans une fonction qui gère une exception entraînera une référence circulaire . Cela empêchera tout ce qui est référencé par une variable locale dans la même fonction ou par le traceback d'être des déchets collectés. [...] si vous avez besoin du traceback, assurez-vous de le supprimer après utilisation de (mieux fait avec un essai ... enfin relevé)

  • mais, du même doc:

    commençant par Python 2.2, ces cycles sont automatiquement récupérés lorsque la collecte des ordures est activée et qu'ils deviennent inaccessibles, mais il reste plus efficace pour éviter de créer des cycles.


d'un autre côté, en vous permettant d'accéder au traceback associé à une exception, Python 3 produire un résultat moins surprenant:

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)

... affichera:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")
342
répondu Sylvain Leroux 2015-06-22 14:43:00

traceback.format_exc() ou sys.exc_info() donnera plus d'information Si c'est ce que vous voulez.

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[0])
534
répondu volting 2016-06-14 21:03:52

si vous déboguez et que vous voulez voir la trace de la pile actuelle, vous pouvez simplement appeler:

traceback.print_stack()

il n'est pas nécessaire de soulever manuellement une exception juste pour l'attraper à nouveau.

165
répondu dimo414 2015-05-19 06:38:36

comment imprimer le traceback complet sans interrompre le programme?

lorsque vous ne voulez pas arrêter votre programme sur une erreur, vous devez gérer cette erreur avec un essai / sauf:

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

pour extraire le traceback complet, nous utiliserons le module traceback de la bibliothèque standard:

import traceback

et de créer un stacktrace décemment compliqué pour démontrer que nous obtenons la pleine stacktrace:

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

def do_something_that_might_error():
    raise_error()

impression

À imprimer le plein de sperme, utiliser le traceback.print_exc la méthode:

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()

qui imprime:

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!

mieux que l'impression, la journalisation:

Cependant, une bonne pratique est d'avoir un enregistreur pour votre module. Il connaîtra le nom du module et pourra changer de niveau (entre autres attributs, tels que handlers)

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

dans ce cas, vous voulez la fonction logger.exception à la place:

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

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

ou peut-être vous voulez juste la chaîne, dans ce cas, vous voulez la fonction traceback.format_exc à la place:

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

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

Conclusion

et pour tous trois options, nous voyons que nous obtenir 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!
59
répondu Aaron Hall 2016-03-23 01:50:19

pour obtenir la précise trace de pile, comme une chaîne, que aurait ont été soulevées si aucun essai/sauf si étaient là pour le enjamber, placez simplement ceci dans le bloc d'exception qui attrape l'exception offensante.

desired_trace = traceback.format_exc(sys.exc_info())

Voici comment l'utiliser (en supposant que flaky_func est défini, et log appelle votre système de journalisation préféré):

import traceback
import sys

try:
    flaky_func()
except KeyboardInterrupt:
    raise
except Exception:
    desired_trace = traceback.format_exc(sys.exc_info())
    log(desired_trace)

C'est une bonne idée d'attraper et de la relancer KeyboardInterrupt s, de sorte que vous pouvez toujours tuer le programme en utilisant Ctrl-C. journalisation est en dehors de la portée de la question, mais une bonne option est logging . Documentation pour les modules sys et traceback .

6
répondu Edward Newell 2015-11-15 18:35:03

vous aurez besoin de mettre l'essai / sauf dans le plus innerloop où l'erreur peut se produire ,i.e.

for i in something:
    for j in somethingelse:
        for k in whatever:
            try:
                something_complex(i, j, k)
            except Exception, e:
                print e
        try:
            something_less_complex(i, j)
        except Exception, e:
            print e

... et ainsi de suite

en d'autres termes, vous devrez envelopper les déclarations qui peuvent échouer dans try/sauf aussi spécifique que possible, dans la boucle la plus interne possible.

5
répondu Ivo van der Wijk 2010-09-13 17:10:16

en plus de la réponse de @Aaron Hall, si vous êtes connecté, mais ne voulez pas utiliser logging.exception() (puisqu'il se connecte au niveau D'erreur), vous pouvez utiliser un niveau inférieur et passer exc_info=True . par exemple

try:
    do_something_that_might_error()
except Exception:
    logger.info('General exception noted.', exc_info=True)
4
répondu Mark McDonald 2018-04-24 03:19:02

vous voulez le module traceback . Il vous permettra d'imprimer des dumps de pile comme Python le fait normalement. En particulier, la fonction print_last affichera la dernière exception et une trace de pile.

2
répondu nmichaels 2010-09-13 17:25:22