python: Comment savoir quel type d'exception s'est produit?

230

J'ai une fonction appelée par le programme principal:

try:
    someFunction()
except:
    print "exception happened!"

mais au milieu de l'exécution de la fonction, il lève une exception, donc il saute à la exceptpartie.

Comment puis-je voir exactement ce qui s'est passé dans le someFunction()qui a provoqué l'exception?

Shang Wang
la source
9
N'utilisez jamais jamais nu except:(sans nu raise), sauf peut-être une fois par programme, et de préférence pas à ce moment-là.
Mike Graham
Si vous utilisez plusieurs exceptclauses, vous n'aurez pas besoin de vérifier le type d'exception, c'est ce qui est généralement fait pour agir en fonction d'un type d'exception spécifique.
Rik Poggi
3
Si vous vous souciez du type d'exception, c'est parce que vous avez déjà considéré quels types d'exception pourraient logiquement se produire.
Karl Knechtel
3
À l'intérieur du exceptbloc, l'exception est disponible via la sys.exc_info()fonction - Cette fonction renvoie un tuple de trois valeurs qui fournissent des informations sur l'exception qui est actuellement gérée.
Piotr Dobrogost

Réponses:

384

Les autres réponses indiquent toutes que vous ne devez pas intercepter les exceptions génériques, mais personne ne semble vouloir vous dire pourquoi, ce qui est essentiel pour comprendre quand vous pouvez enfreindre la "règle". Voici une explication. Fondamentalement, c'est pour que vous ne vous cachiez pas:

Donc, tant que vous prenez soin de ne rien faire de tout cela, il est normal d'attraper l'exception générique. Par exemple, vous pouvez fournir des informations sur l'exception à l'utilisateur d'une autre manière, comme:

  • Présenter les exceptions sous forme de boîtes de dialogue dans une interface graphique
  • Transférer des exceptions d'un thread ou d'un processus de travail vers le thread ou le processus de contrôle dans une application multithreading ou multiprocessing

Alors, comment intercepter l'exception générique? Il y a plusieurs façons. Si vous voulez juste l'objet d'exception, faites-le comme ceci:

try:
    someFunction()
except Exception as ex:
    template = "An exception of type {0} occurred. Arguments:\n{1!r}"
    message = template.format(type(ex).__name__, ex.args)
    print message

Assurez- vous d' message être porté à l'attention de l'utilisateur d'une manière difficile à manquer! L'imprimer, comme illustré ci-dessus, peut ne pas être suffisant si le message est enterré dans de nombreux autres messages. Ne pas attirer l'attention des utilisateurs équivaut à avaler toutes les exceptions, et s'il y a une impression que vous auriez dû retirer après avoir lu les réponses sur cette page, c'est que ce n'est pas une bonne chose . La fin du bloc except avec une raiseinstruction résoudra le problème en relançant de manière transparente l'exception qui a été interceptée.

La différence entre ce qui précède et l'utilisation juste except:sans aucun argument est double:

  • Un nu except:ne vous donne pas l'objet d'exception à inspecter
  • Les exceptions SystemExit, KeyboardInterruptet GeneratorExitne sont pas prises par le code ci-dessus, qui est généralement ce que vous voulez. Voir la hiérarchie des exceptions .

Si vous voulez également la même trace de pile que vous obtenez si vous n'attrapez pas l'exception, vous pouvez l'obtenir comme ceci (toujours à l'intérieur de la clause except):

import traceback
print traceback.format_exc()

Si vous utilisez le loggingmodule, vous pouvez imprimer l'exception dans le journal (avec un message) comme ceci:

import logging
log = logging.getLogger()
log.exception("Message for you, sir!")

Si vous voulez creuser plus profondément et examiner la pile, regardez les variables etc., utilisez la post_mortemfonction du pdbmodule à l'intérieur du bloc except:

import pdb
pdb.post_mortem()

J'ai trouvé cette dernière méthode inestimable lors de la recherche de bugs.

Lauritz V. Thaulow
la source
1
traceback.print_exc () ferait la même chose que votre "" .join-chose plus compliqué, je pense.
Gurgeh
1
@Gurgeh Oui, mais je ne sais pas s'il veut l'imprimer ou l'enregistrer dans un fichier ou l'enregistrer ou faire autre chose avec.
Lauritz V. Thaulow
Je n'ai pas déçu, mais je dirais que c'est parce que vous auriez dû mettre un énorme gras au début en disant que vous ne devriez pas avoir besoin de tout cela, mais voici comment cela pourrait être fait . Et peut-être parce que vous proposez d'attraper l'exception générique.
Rik Poggi
10
@Rik Je pense que vous pourriez très bien avoir besoin de tout cela. Par exemple, si vous avez un programme avec une interface graphique et un backend, et que vous souhaitez présenter toutes les exceptions du backend sous forme de messages GUI au lieu de laisser votre programme se fermer avec une trace de pile. Dans ce cas, vous devez intercepter l' exception générique , créer un texte de traceback pour la boîte de dialogue, consigner également l'exception et, en mode débogage, entrer post-mortem.
Lauritz V. Thaulow
18
@RikPoggi: Pensée naïve. Il existe de nombreuses circonstances raisonnables lorsque vous devez intercepter des exceptions du code de quelqu'un d'autre et que vous ne savez pas quelles exceptions seront déclenchées.
stackoverflowuser2010
63

Obtenez le nom de la classe à laquelle appartient l'objet d'exception:

e.__class__.__name__

et l'utilisation de la fonction print_exc () imprimera également la trace de la pile qui est une information essentielle pour tout message d'erreur.

Comme ça:

from traceback import print_exc

class CustomException(Exception): pass

try:
    raise CustomException("hi")
except Exception, e:
    print 'type is:', e.__class__.__name__
    print_exc()
    # print "exception happened!"

Vous obtiendrez une sortie comme celle-ci:

type is: CustomException
Traceback (most recent call last):
  File "exc.py", line 7, in <module>
    raise CustomException("hi")
CustomException: hi

Et après impression et analyse, le code peut décider de ne pas gérer d'exception et d'exécuter simplement raise:

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        raise
    print "handling exception"

Production:

special case of CustomException not interfering

Et l'interprète imprime l'exception:

Traceback (most recent call last):
  File "test.py", line 9, in <module>
    calculate()
  File "test.py", line 6, in calculate
    raise CustomException("hi")
__main__.CustomException: hi

Après que raisel'exception d'origine continue de se propager plus haut dans la pile des appels. ( Méfiez-vous des pièges possibles ) Si vous soulevez une nouvelle exception, il se soucie d'une nouvelle trace de pile (plus courte).

from traceback import print_exc

class CustomException(Exception): pass

def calculate():
    raise CustomException("hi")

try:
    calculate()
except Exception, e:
    if e.__class__ == CustomException:
        print 'special case of', e.__class__.__name__, 'not interfering'
        #raise CustomException(e.message)
        raise e
    print "handling exception"

Production:

special case of CustomException not interfering
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    raise CustomException(e.message)
__main__.CustomException: hi    

Remarquez comment traceback n'inclut pas la calculate()fonction de la ligne 9qui est à l'origine de l'exception d'origine e.

Alex
la source
Si vous souhaitez stocker le retraçage comme une chaîne, vous pouvez utiliser traceback.format_exc()aussi bien
Stevoisiak
1
e.__class__.__name__est-ce la même chose que type(e).__name__, comme le suggère la réponse ci-dessus?
information_interchange
1
@information_interchange oui. Le contenu des questions et des réponses acceptées a complètement changé au fil du temps. Il est dommage que les autres participants ne soient pas informés par les machines SO :(
Alex
14

Vous ne devriez généralement pas intercepter toutes les exceptions possibles try: ... exceptcar cela est trop large. Attrapez simplement ceux qui devraient arriver pour une raison quelconque. Si vous devez vraiment, par exemple si vous voulez en savoir plus sur un problème lors du débogage, vous devez

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.
hochl
la source
17
L'utilisation du mot «jamais» ici n'a jamais été aussi mauvaise. J'utilise try: ... except Exception:beaucoup de choses, par exemple l'utilisation de bibliothèques dépendantes du réseau ou une masseuse de données qui peut lui envoyer des trucs bizarres. Naturellement, j'ai également une journalisation appropriée. Ceci est crucial pour permettre au programme de continuer à fonctionner en cas d'une seule erreur dans les données d'entrée.
thnee
3
Avez-vous déjà essayé de détecter toutes les exceptions pouvant être déclenchées lors de l'envoi d'un e-mail à l'aide de smtplib?
linusg
1
Il peut y avoir des cas spéciaux où la capture de toutes les exceptions est nécessaire, mais de manière générale, vous devez simplement intercepter ce que vous attendez afin de ne pas masquer accidentellement des erreurs que vous n'aviez pas anticipées. Une bonne journalisation est également une bonne idée.
hochl
1
Il est parfaitement raisonnable d'attraper toutes les exceptions. Si vous appelez une bibliothèque tierce, vous ne savez peut-être pas quelles exceptions seront levées dans cette bibliothèque. Dans un tel cas, le seul recours consiste à intercepter toutes les exceptions, par exemple à les enregistrer dans un fichier.
stackoverflowuser2010
Ok ok vous avez raison, je reformulerai ma réponse pour préciser qu'il existe des cas d'utilisation valables pour un fourre-tout.
hochl
10

Sauf si somefunctionc'est une très mauvaise fonction héritée codée, vous ne devriez pas avoir besoin de ce que vous demandez.

Utilisez plusieurs exceptclauses pour gérer de différentes manières différentes exceptions:

try:
    someFunction()
except ValueError:
    # do something
except ZeroDivision:
    # do something else

Le point principal est que vous ne devez pas intercepter une exception générique, mais uniquement celles dont vous avez besoin. Je suis sûr que vous ne voulez pas masquer les erreurs ou les bugs inattendus.

Rik Poggi
la source
8
Si vous utilisez une bibliothèque tierce, vous ne savez peut-être pas quelles exceptions seront déclenchées à l'intérieur de celle-ci. Comment pouvez-vous tous les attraper individuellement?
stackoverflowuser2010
8

La plupart des réponses pointent vers la except (…) as (…):syntaxe (à juste titre) mais en même temps, personne ne veut parler d'un éléphant dans la pièce, où l'éléphant est sys.exc_info()fonction. À partir de la documentation du module sys (c'est moi qui souligne):

Cette fonction renvoie un tuple de trois valeurs qui fournissent des informations sur l'exception qui est actuellement gérée.
(…)
Si aucune exception n'est gérée n'importe où sur la pile, un tuple contenant trois valeurs None est retourné. Sinon, les valeurs renvoyées sont (type, valeur, traceback). Leur signification est la suivante: type obtient le type de l'exception gérée (une sous-classe de BaseException); value obtient l'instance d'exception (une instance du type d'exception); traceback obtient un objet traceback (voir le Manuel de référence) qui encapsule la pile d'appels au point où l'exception s'est produite à l'origine.

Je pense que le sys.exc_info()pourrait être considéré comme la réponse la plus directe à la question initiale de Comment savoir quel type d'exception s'est produit?

Piotr Dobrogost
la source
1
C'est la bonne réponse pour moi car elle résout le problème de l'exception qui se produit, alors que dois-je mettre au lieu de nu except. Juste pour être complet, exctype, value = sys.exc_info()[:2]vous dira le type d'exception qui peut ensuite être utilisé sur le except.
Ondrej Burkert
5

essayez: someFunction () sauf Exception, exc:

#this is how you get the type
excType = exc.__class__.__name__

#here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)

#It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))
wp-overwatch.com
la source
-1 comme utilisation exc.__class__.__name__avait déjà été suggéré dans la réponse d'Alex - stackoverflow.com/a/9824060/95735
Piotr Dobrogost
3

Ces réponses conviennent très bien pour le débogage, mais pour tester l'exception par programme, elles isinstance(e, SomeException)peuvent être utiles, car elles testent également les sous-classes de SomeException, afin que vous puissiez créer des fonctionnalités qui s'appliquent aux hiérarchies d'exceptions.

Chris
la source
1

Voici comment je gère mes exceptions. L'idée est d'essayer de résoudre le problème si c'est facile, et d'ajouter plus tard une solution plus souhaitable si possible. Ne résolvez pas le problème dans le code qui génère l'exception, ou ce code perd la trace de l'algorithme d'origine, qui devrait être écrit au point. Cependant, transmettez les données nécessaires pour résoudre le problème et renvoyez un lambda au cas où vous ne pourriez pas résoudre le problème en dehors du code qui le génère.

path = 'app.p'

def load():
    if os.path.exists(path):
        try:
            with open(path, 'rb') as file:
                data = file.read()
                inst = pickle.load(data)
        except Exception as e:
            inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
    else:
        inst = App()
    inst.loadWidgets()

# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
    class_name = e.__class__.__name__
    print(class_name + ': ' + str(e))
    print('\t during: ' + during)
    return easy

Pour l'instant, comme je ne veux pas penser de manière tangible à l'objectif de mon application, je n'ai ajouté aucune solution compliquée. Mais à l'avenir, quand j'en saurai plus sur les solutions possibles (puisque l'application est plus conçue), je pourrais ajouter un dictionnaire de solutions indexées parduring .

Dans l'exemple illustré, une solution pourrait être de rechercher des données d'application stockées ailleurs, par exemple si le fichier «app.p» a été supprimé par erreur.

Pour l'instant, étant donné que l'écriture du gestionnaire d'exceptions n'est pas une idée intelligente (nous ne connaissons pas encore les meilleures façons de le résoudre, car la conception de l'application évoluera), nous renvoyons simplement la solution simple qui consiste à agir comme si nous étions en cours d'exécution l'application pour la première fois (dans ce cas).

RésuméAlgèbreLearner
la source
0

Pour ajouter à la réponse de Lauritz, j'ai créé un décorateur / wrapper pour la gestion des exceptions et les journaux de wrapper quel type d'exception s'est produit.

class general_function_handler(object):
    def __init__(self, func):
        self.func = func
    def __get__(self, obj, type=None):
        return self.__class__(self.func.__get__(obj, type))
    def __call__(self, *args, **kwargs):
        try:
            retval = self.func(*args, **kwargs)
        except Exception, e :
            logging.warning('Exception in %s' % self.func)
            template = "An exception of type {0} occured. Arguments:\n{1!r}"
            message = template.format(type(e).__name__, e.args)
            logging.exception(message)
            sys.exit(1) # exit on all exceptions for now
        return retval

Cela peut être appelé sur une méthode de classe ou une fonction autonome avec le décorateur:

@general_function_handler

Voir mon blog sur pour l'exemple complet: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/

rirwin
la source
0

Vous pouvez commencer comme recommandé par Lauritz, avec:

except Exception as ex:

et juste pour print exaimer:

try:
    #your try code here
except Exception as ex:
    print ex
Gura
la source
Pouvez-vous élaborer un peu pour que votre réponse soit autonome?
GHC
1
bien sûr: vous pouvez imprimer l'exception interceptée comme ceci: essayez: #votre code d'essai ici sauf Exception comme ex: print ex maintenant l'erreur sera imprimée
Gura
-2

L'exception réelle peut être capturée de la manière suivante:

try:
    i = 1/0
except Exception as e:
    print e

Vous pouvez en savoir plus sur les exceptions à partir du didacticiel Python .

Kevin Coffey
la source
-2

Votre question est: "Comment puis-je voir exactement ce qui s'est passé dans la fonction someFunction () qui a provoqué l'exception?"

Il me semble que vous ne demandez pas comment gérer les exceptions imprévues dans le code de production (comme de nombreuses réponses le supposent), mais comment savoir ce qui cause une exception particulière pendant le développement.

Le moyen le plus simple consiste à utiliser un débogueur qui peut s'arrêter là où l'exception non interceptée se produit, de préférence ne pas quitter, afin que vous puissiez inspecter les variables. Par exemple, PyDev dans l'IDE open source Eclipse peut le faire. Pour activer cela dans Eclipse, ouvrez la perspective Débogage, sélectionnez Manage Python Exception Breakpointsdans le Runmenu et cochez Suspend on uncaught exceptions.


la source
-4

Abstenez-vous simplement d'attraper l'exception et le traceback que Python imprime vous dira quelle exception s'est produite.

Mike Graham
la source