I know that if I want to re-raise an exception, I simple use raise
without arguments in the respective except
block. But given a nested expression like
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
how can I re-raise the SomeError
without breaking the stack trace? raise
alone would in this case re-raise the more recent AlsoFailsError
. Or how could I refactor my code to avoid this issue?
You can store the exception type, value, and traceback in local variables and use the three-argument form of raise
:
try:
something()
except SomeError:
t, v, tb = sys.exc_info()
try:
plan_B()
except AlsoFailsError:
raise t, v, tb
In Python 3 the traceback is stored in the exception, so raise e
will do the (mostly) right thing:
try:
something()
except SomeError as e:
try:
plan_B()
except AlsoFailsError:
raise e
The only problem with the above is that it will produce a slightly misleading traceback that tells you that SomeError
occurred while handling AlsoFailsError
(because of raise e
inside except AlsoFailsError
), where in fact the almost exact opposite occurred - we handled AlsoFailsError
while trying to recover from SomeError
. To disable this behavior and get a traceback that never mentions AlsoFailsError
, replace raise e
with raise e from None
.
Even if the accepted solution is right, it's good to point to the Six library which has a Python 2+3 solution, using six.reraise
.
six.reraise(exc_type, exc_value, exc_traceback=None)
Reraise an exception, possibly with a different traceback. [...]
So, you can write:
import six
try:
something()
except SomeError:
t, v, tb = sys.exc_info()
try:
plan_B()
except AlsoFailsError:
six.reraise(t, v, tb)
As per Drew McGowen's suggestion, but taking care of a general case (where a return value s
is present), here's an alternative to user4815162342's answer:
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
Python 3.5+ attaches the traceback information to the error anyway, so it's no longer necessary to save it separately.
>>> 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
>>>
来源:https://stackoverflow.com/questions/18188563/how-to-re-raise-an-exception-in-nested-try-except-blocks