web-dev-qa-db-fra.com

Python chaînage d'exceptions

Existe-t-il un moyen standard d'utiliser des chaînes d'exceptions en Python? Comme l'exception Java 'causée par'?

Voici quelques informations.

J'ai un module avec une classe d'exception principale DSError:

 class DSError(Exception):
     pass

Quelque part dans ce module, il y aura:

try:
    v = my_dict[k]
    something(v)
except KeyError as e:
    raise DSError("no key %s found for %s" % (k, self))
except ValueError as e:
    raise DSError("Bad Value %s found for %s" % (v, self))
except DSError as e:
    raise DSError("%s raised in %s" % (e, self))

Fondamentalement, cet extrait ne devrait renvoyer que DSError et me dire ce qui s'est passé et pourquoi. Le fait est que le bloc try peut générer de nombreuses autres exceptions, donc je préférerais que je puisse faire quelque chose comme:

try:
    v = my_dict[k]
    something(v)
except Exception as e:
    raise DSError(self, v, e)  # Exception chained...

Est-ce que c'est une méthode Pythonique standard? Je n'ai pas vu de chaînes d'exceptions dans d'autres modules, comment cela se fait-il en Python?

73
Ayman

chaînage d'exception est uniquement disponible en Python 3, où vous pouvez écrire:

try:
    v = {}['a']
except KeyError as e:
    raise ValueError('failed') from e

qui donne une sortie comme

Traceback (most recent call last):
  File "t.py", line 2, in <module>
    v = {}['a']
KeyError: 'a'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "t.py", line 4, in <module>
    raise ValueError('failed') from e
ValueError: failed

Dans la plupart des cas, vous n'avez même pas besoin du from; Python 3 affichera par défaut toutes les exceptions qui se sont produites pendant la gestion des exceptions, comme ceci:

Traceback (most recent call last):
  File "t.py", line 2, in <module>
    v = {}['a']
KeyError: 'a'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "t.py", line 4, in <module>
    raise ValueError('failed')
ValueError: failed

Ce que vous pouvez faire dans Python 2 consiste à ajouter des attributs personnalisés à votre classe d'exception, comme:

class MyError(Exception):
    def __init__(self, message, cause):
        super(MyError, self).__init__(message + u', caused by ' + repr(cause))
        self.cause = cause

try:
    v = {}['a']
except KeyError as e:
    raise MyError('failed', e)
106
phihag

C'est bien ce que vous demandez?

class MyError(Exception):
    def __init__(self, other):
        super(MyError, self).__init__(other.message)

>>> try:
...     1/0
... except Exception, e:
...     raise MyError(e)
Traceback (most recent call last):
  File "<pyshell#27>", line 4, in <module>
    raise MyError(e)
MyError: division by zero

Si vous souhaitez stocker l'objet d'exception d'origine, vous pouvez certainement le faire dans le __init__ De votre propre classe d'exception. Vous pouvez en fait vouloir stocker le traceback car l'objet exception lui-même ne fournit pas beaucoup d'informations utiles sur l'endroit où l'exception s'est produite:

class MyError(Exception):
    def __init__(self, other):
        self.traceback = sys.exc_info()
        super(MyError, self).__init__(other.message)

Après cela, vous pouvez accéder à l'attribut traceback de votre exception pour obtenir des informations sur l'exception d'origine. (Python 3 fournit déjà cela en tant qu'attribut __traceback__ D'un objet d'exception.)

5
BrenBarn