Comment relancer une exception dans try/excepté les blocs imbriqués?

je sais que si je veux relancer une exception, Je l'utilise simplement raise sans arguments dans le bloc respectif except . 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 briser la trace de la pile? raise relèverait dans ce cas le plus récent AlsoFailsError . Ou comment puis-je modifier mon code pour éviter ce problème?

42
demandé sur Tobias Kienzler 2013-08-12 17:42:39

4 réponses

vous pouvez stocker le type d'exception, la valeur, et traceback dans les variables locales et utiliser le forme à trois arguments de raise :

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

en Python 3 le traceback est stocké dans l'exception, donc raise e fera la (la plupart du temps) 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'il va produire un traceback légèrement trompeur qui vous dit que SomeError s'est produit alors que manipulation AlsoFailsError (à cause de raise e à l'intérieur de Except AlsoFailsError ), où en fait le contraire presque exact s'est produit - nous avons manipulé AlsoFailsError tout en essayant de récupérer de SomeError . Pour désactiver ce comportement et obtenir un traceback qui ne mentionne jamais AlsoFailsError , remplacer raise e par raise e from None .

62
répondu user4815162342 2017-04-26 21:06:08

comme pour 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 à réponse de l'utilisateur 4815162342 :

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
7
répondu Tobias Kienzler 2017-05-23 12:10:39

même si la solution acceptée est correcte, il est bon de pointer vers la bibliothèque Six qui a une solution Python 2+3, en utilisant six.reraise .

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

Relancer une exception, éventuellement, de avec un traceback 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)
7
répondu Laurent LAPORTE 2017-09-28 10:27:19

Python 3.5+ attache l'information de retraçage à l'erreur de toute façon, il n'est donc plus nécessaire de la sauvegarder 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
>>> 
1
répondu Matthias Urlichs 2018-04-05 22:32:19