Je sais que si je veux re-lever une exception, je l'utilise simplement raise
sans arguments dans le except
bloc respectif . 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-lever le SomeError
sans casser la trace de la pile? raise
seul, dans ce cas, relèverait le plus récent AlsoFailsError
. Ou comment pourrais-je refactoriser mon code pour éviter ce problème?
plan_B
dans une autre fonction qui retourneTrue
sur le succès etFalse
sur l'exception? Ensuite, leexcept
bloc extérieur pourrait simplement êtreif not try_plan_B(): raise
arg
et j'essaierais d'appeler,arg.plan_B()
ce qui pourrait entraîner unAttributeError
problème en raison dearg
ne pas fournir de plan Bplan_B
de lever des exceptionsRéponses:
À partir de Python 3, le traçage est stocké dans l'exception, donc un simple
raise e
fera la bonne chose (principalement):Le traçage produit inclura un avis supplémentaire qui
SomeError
s'est produit lors de la manipulationAlsoFailsError
(en raison d'raise e
être à l'intérieurexcept AlsoFailsError
). Ceci est trompeur car ce qui s'est réellement passé est l'inverse: nous l'avons rencontréAlsoFailsError
et géré en essayant de nous en remettreSomeError
. Pour obtenir une trace qui n'inclut pasAlsoFailsError
, remplacezraise e
parraise e from None
.Dans Python 2, vous stockez le type d'exception, la valeur et le suivi dans des variables locales et utilisez la forme à trois arguments de
raise
:la source
raise self.exc_info[1], None, self.exc_info[2]
aprèsself.exc_info = sys.exc_info()
- mise[1]
en première position pour une raison quelconqueraise t, None, tb
perdra la valeur de l'exception et forceraraise
à la ré-instancier à partir du type, vous donnant une valeur d'exception moins spécifique (ou simplement incorrecte). Par exemple, si l'exception levée estKeyError("some-key")
, elle se relèvera simplementKeyError()
et omettra la clé manquante exacte du suivi.raise v.with_traceback(tb)
. (Votre commentaire en dit même autant, sauf qu'il propose de ré-instancier la valeur.)sys.exc_info()
dans une variable locale avait du sens avant Python 2.0 (publié il y a 13 ans), mais est aujourd'hui ridicule. Le Python moderne serait presque inutile sans le collecteur de cycles, car chaque bibliothèque Python non triviale crée des cycles sans pause et dépend de leur nettoyage correct.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
.Ainsi, vous pouvez écrire:
la source
six.raise_from
si vous souhaitez inclure des informations qui ontplan_B()
également échoué.six.raise_from
vous créez une nouvelle exception qui est liée à une précédente, vous ne relancez pas , donc la trace est différente.reraise
avez l'impression quesomething()
jetéSomeError
, si vousraise_from
savez aussi que cela a causéplan_B()
d'être exécuté, mais en jetant leAlsoFailsError
. Cela dépend donc du cas d'utilisation. Je pense queraise_from
cela facilitera le débogageSelon la suggestion de Drew McGowen , mais en prenant soin d'un cas général (où une valeur de retour
s
est présente), voici une alternative à la réponse de user4815162342 :la source
raise from
, donc la trace de pile me laisserait également voir le plan B échouer. Qui peut être émulé dans Python 2 d'ailleurs.Python 3.5+ attache de toute façon les informations de traçage à l'erreur, il n'est donc plus nécessaire de l'enregistrer séparément.
la source
except
. Mais vous avez raison, quand je remplaceerr = e
par, disons,raise AttributeError
vous obtenez d'abord laSyntaxError
trace de pile, suivie de aDuring handling of the above exception, another exception occurred:
et de laAttributeError
trace de pile. Bon à savoir, mais malheureusement, on ne peut pas compter sur l'installation de 3.5+. PS: ff verstehen nicht-Deutsche vermutlich nicht;)