Tejas Manohar
Tejas Manohar

Reputation: 974

How to access re-raised exception in Python 3?

In Python 3, there's a useful raise ... from ... feature to re-raise an exception. That said, how do you find the original (/ re-raised) exception from the raised exception? Here's a (silly) example with comments to demonstrate what I mean--

def some_func():
    try:
      None() # TypeError: 'NoneType' object is not callable
    except as err:
      raise Exception("blah") from err

try:
    some_func()
except as err:
    # how can I access the original exception (TypeError)?

Upvotes: 2

Views: 940

Answers (2)

zvone
zvone

Reputation: 19352

Whenever an exception is raised from an exception handler (the except clause), the original exception will bestored in new exception's __context__.

Whenever an exception is raised using from syntax, the exception specified in from will be saved in the __cause__ attribute of the new exception.

In the usual use case, that amounts to both __cause__ and __context__ containing the original exception:

def f():
    try:
        raise Exception('first exception')
    except Exception as e:
        raise Exception('second exception') from e

try:
    f()
except Exception as e:
    print('This exception', e)
    print('Original exception', e.__context__)
    print('Also original exception', e.__cause__)

Here is also an example of when __context__ is set:

try:
    raise Exception('first exception')
except Exception as e:
    raise Exception('second exception')

and an example of when __cause__ is set:

e = Exception('first exception')
raise Exception('second exception') from e

Upvotes: 2

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160417

It's in the __cause__ attribute of the raised exception. Taken from the docs on the raise statement it says regarding raise ... from ...:

The from clause is used for exception chaining: if given, the second expression must be another exception class or instance, which will then be attached to the raised exception as the __cause__ attribute (which is writable). If the raised exception is not handled, both exceptions will be printed.

So, in your given scenario, repring the __cause__ attribute:

def some_func():
    try:
      None() # TypeError: 'NoneType' object is not callable
    except TypeError as err:
      raise Exception("blah") from err

try:
    some_func()
except Exception as er:
    print(repr(er.__cause__))

Will print out:

TypeError("'NoneType' object is not callable",)

Upvotes: 4

Related Questions