web-dev-qa-db-fra.com

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

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 déclenche une exception et saute à la partie except.

Comment voir exactement ce qui s’est passé dans la someFunction() qui a provoqué l’exception?

194
Shang Wang

Les autres réponses indiquent toutes que vous ne devriez 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". Ici est une explication. En gros, c'est pour ne pas vous cacher:

Donc, tant que vous prenez soin de ne faire aucune de ces choses, vous pouvez capturer l'exception générique. Par exemple, vous pouvez fournir des informations sur l'exception à l'utilisateur d'une autre manière, par exemple:

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

Alors, comment attraper l'exception générique? Il y a plusieurs façons. Si vous voulez juste l'objet exception, procédez comme suit:

_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 que message est porté à l’attention de l’utilisateur de manière difficile à manquer! L'imprimer, comme indiqué 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 ya une impression à laquelle vous auriez dû revenir après avoir lu les réponses sur cette page, c’est que ce n'est pas une bonne chose . Terminer le bloc except avec une instruction raise 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 de _except:_ sans aucun argument est double:

  • Un _except:_ nu ne vous donne pas l'objet exception à inspecter
  • Les exceptions SystemExit, KeyboardInterrupt et GeneratorExit ne sont pas interceptées par le code ci-dessus, qui correspond généralement à ce que vous souhaitez. Voir le hiérarchie des exceptions .

Si vous voulez également le même stacktrace que vous obtenez si vous ne capturez pas l'exception, vous pouvez l'obtenir comme ceci (toujours dans la clause except):

_import traceback
print traceback.format_exc()
_

Si vous utilisez le module logging , 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, examiner les variables, etc., utilisez la fonction post_mortem du module pdb à l'intérieur du bloc d'exception:

_import pdb
pdb.post_mortem()
_

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

320
Lauritz V. Thaulow

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

e.__class__.__name__

et utiliser la fonction print_exc () affichera également la trace de 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 ceci:

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

Et après l’impression et l’analyse, le code peut décider de ne pas gérer l’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"

Sortie:

special case of CustomException not interfering

Et interprète imprime 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 raise l'exception d'origine continue à se propager plus haut dans la pile d'appels. (Attention aux pièges possibles) Si vous déclenchez une nouvelle exception, une nouvelle trace (plus courte) de pile est générée.

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"

Sortie:

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    

Notez que traceback n'inclut pas la fonction calculate() de la ligne 9, qui est l'origine de l'exception d'origine e.

43
Alex

En général, vous ne devez pas intercepter toutes les exceptions possibles avec try: ... except, car celle-ci est trop large. Attrapez simplement ceux qui devraient se produire pour une raison quelconque. Si vous en avez vraiment besoin, par exemple si vous voulez en savoir plus sur un problème lors du débogage, vous devriez le faire.

try:
    ...
except Exception as ex:
    print ex # do whatever you want for debugging.
    raise    # re-raise exception.
13
hochl

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

Utilisez plusieurs clauses except 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 devriez pas attraper l'exception générique, mais seulement celles dont vous avez besoin. Je suis sûr que vous ne voulez pas masquer des erreurs ou des bogues inattendus.

8
Rik Poggi

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

Cette fonction retourne un tuple de trois valeurs fournissant des informations sur l'exception en cours de traitement.
(…)
Si aucune exception n'est gérée quelque part sur la pile, un tuple contenant trois valeurs Aucune est renvoyé. Sinon, les valeurs renvoyées sont (type, valeur, traceback). Leur signification est la suivante: type obtient le type de l'exception à gérer (une sous-classe de BaseException); valeur 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 la sys.exc_info() pourrait être traitée comme la réponse la plus directe à la question initiale de Comment savoir quel type d’exception s’est produit?

7
Piotr Dobrogost

try: 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))
5
Mr. Me

Voici comment je gère mes exceptions. L'idée est d'essayer de résoudre le problème si cela est facile, puis d'ajouter si possible une solution plus souhaitable. Ne résolvez pas le problème dans le code qui génère l'exception, ou ce code perdra la trace de l'algorithme d'origine, qui devrait être écrit à point. Toutefois, 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 tangente à l'objectif de mon application, je n'ai pas ajouté de solutions compliquées. Mais à l'avenir, quand j'en saurai plus sur les solutions possibles (puisque l'application est conçue davantage), je pourrais ajouter un dictionnaire de solutions indexées par during.

Dans l'exemple présenté, une solution pourrait consister à rechercher des données d'application stockées ailleurs, par exemple si le fichier 'app.p' a été supprimé par erreur.

Pour l'instant, comme écrire le gestionnaire d'exceptions n'est pas une bonne idée (nous ne connaissons pas encore les meilleures solutions, car la conception de l'application évoluera), nous renvoyons simplement la solution de facilité qui consiste à agir comme si nous exécutions l'application pour la première fois (dans ce cas).

Vous pouvez commencer comme recommandé par Lauritz, avec:

except Exception as ex:

et puis juste pour print ex comme ceci:

try:
    #your try code here
except Exception as ex:
    print ex
0
Gura

Pour ajouter à la réponse de Lauritz, j'ai créé un décorateur/wrapper pour la gestion des exceptions et le wrapper enregistre le type d'exception survenue.

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é avec une méthode de classe ou une fonction autonome avec le décorateur:

@general_function_handler

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

0
rirwin