177 votes

Comment lever à nouveau une exception dans des blocs try/except imbriqués ?

Je sais que si je veux relancer une exception, j'utilise simplement raise sans arguments dans les except bloc. Mais avec une expression imbriquée comme

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e  # I'd like to raise the SomeError as if plan_B()
                 # didn't raise the AlsoFailsError

comment puis-je relancer le SomeError sans casser la trace de la pile ? raise seul relèverait dans ce cas le plus récent AlsoFailsError . Ou comment pourrais-je refactoriser mon code pour éviter ce problème ?

213voto

user4815162342 Points 27348

À partir de Python 3, la traceback est stockée dans l'exception, de sorte qu'une simple commande raise e fera la (presque) bonne chose :

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e  # or raise e from None - see below

L'historique produit comprendra un avis supplémentaire indiquant que SomeError s'est produit lors de la manipulation AlsoFailsError (en raison de raise e être à l'intérieur except AlsoFailsError ). Ceci est trompeur car ce qui s'est réellement passé est l'inverse - nous avons rencontré AlsoFailsError et l'a manipulé, tout en essayant de se remettre de l'accident. SomeError . Pour obtenir un retour de trace qui n'inclut pas AlsoFailsError , remplacer raise e con raise e from None .


En Python 2, vous stockeriez le type d'exception, la valeur et la traceback dans des variables locales et utiliseriez la fonction forme à trois arguments de raise :

try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        raise t, v, tb

29voto

Laurent LAPORTE Points 10193

Même si le solution acceptée est juste, il est bon de pointer du doigt la Six qui dispose d'une solution Python 2+3, en utilisant six.reraise .

six. reraise ( exc_type , exc_value , exc_traceback \=None)

Raisonnez une exception, éventuellement avec une traceback différente. [...]

Donc, vous pouvez écrire :

import six

try:
    something()
except SomeError:
    t, v, tb = sys.exc_info()
    try:
        plan_B()
    except AlsoFailsError:
        six.reraise(t, v, tb)

18voto

Tobias Kienzler Points 3769

Conformément à Suggestion de Drew McGowen mais en prenant soin d'un cas général (où une valeur de retour s est présent), voici une alternative à La réponse de user4815162342 :

try:
    s = something()
except SomeError as e:
    def wrapped_plan_B():
        try:
            return False, plan_B()
        except:
            return True, None
    failed, s = wrapped_plan_B()
    if failed:
        raise

9voto

Matthias Urlichs Points 441

Python 3.5+ attache les informations de traçage à l'erreur de toute façon, il n'est donc plus nécessaire de les enregistrer séparément.

>>> def f():
...   try:
...     raise SyntaxError
...   except Exception as e:
...     err = e
...     try:
...       raise AttributeError
...     except Exception as e1:
...       raise err from None
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in f
  File "<stdin>", line 3, in f
SyntaxError: None
>>>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X