How to re-raise an exception in nested try/except blocks?

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?

7

4 Answers

As of Python 3, the traceback is stored in the exception, so a simple raise e will do the (mostly) right thing:

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

The traceback produced will include an additional notice that SomeError occurred while handling AlsoFailsError (because of raise e being inside except AlsoFailsError). This is misleading because what actually happened is the other way around - we encountered AlsoFailsError, and handled it, while trying to recover from SomeError. To obtain a traceback that doesn't include AlsoFailsError, replace raise e with raise e from None.


In Python 2 you'd 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
15

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)
3

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
2

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
>>> 
4

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like