web-dev-qa-db-fra.com

Comment re-lever 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 le bloc except correspondant. Mais étant donné 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 re-augmenter le SomeError sans casser la trace de la pile? raise seul aurait dans ce cas ré-augmenter le plus récent AlsoFailsError. Ou comment pourrais-je refactoriser mon code pour éviter ce problème?

73
Tobias Kienzler

Vous pouvez stocker le type, la valeur et la traceback de l'exception dans des variables locales et utiliser la forme à trois arguments de raise :

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

Dans Python 3, la trace est stockée dans l'exception, donc raise e va faire (principalement) la bonne chose:

try:
    something()
except SomeError as e:
    try:
        plan_B()
    except AlsoFailsError:
        raise e

Le seul problème avec ce qui précède est qu’elle produira un traçage légèrement trompeur qui vous indiquera que SomeError s’est produit lors du traitement de AlsoFailsError (à cause de raise e à l'intérieur except AlsoFailsError), où l’inverse presque exact s’est produit - nous avons géré AlsoFailsError tout en essayant de récupérer de SomeError. Pour désactiver ce comportement et obtenir une trace qui ne mentionne jamais AlsoFailsError, remplacez raise e avec raise e from None.

88
user4815162342

Même si le solution acceptée est correct, il est bon de pointer sur la bibliothèque Six qui a Python 2 + 3, en utilisant - six.reraise .

six. relancer ( exc_type , exc_value , exc_traceback = None)

Relancez une exception, éventuellement avec un retraçage différent. [...]

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)
15
Laurent LAPORTE

Selon suggestion de Drew McGowen , mais en prenant en compte un cas général (où une valeur de retour s est présente), voici une alternative à réponse de l'utilisateur4815162342 :

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
8
Tobias Kienzler

Python 3.5+ associe néanmoins les informations de trace à l'erreur, 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
>>> 
3
Matthias Urlichs