Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: How to catch inner exception of exception chain?

Consider the simple example:

def f():
    try:
        raise TypeError
    except TypeError:
        raise ValueError

f()

I want to catch TypeError object when ValueError is thrown after f() execution. Is it possible to do it?

If I execute function f() then python3 print to stderr all raised exceptions of exception chain (PEP-3134) like

Traceback (most recent call last):
  File "...", line 6, in f
    raise TypeError
TypeError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "...", line 11, in <module>
    f()
  File "...", line 8, in f
    raise ValueError
ValueError

So I would get the list of all exceptions of exception chain or check if exception of some type (TypeError in the above example) exists in exception chain.

like image 1000
kupgov Avatar asked Aug 08 '16 08:08

kupgov


1 Answers

Python 3 has a beautiful syntactic enhancement on exceptions handling. Instead of plainly raising ValueError, you should raise it from a caught exception, i.e.:

try:
    raise TypeError('Something awful has happened')
except TypeError as e:
    raise ValueError('There was a bad value') from e

Notice the difference between the tracebacks. This one uses raise from version:

Traceback (most recent call last):
  File "/home/user/tmp.py", line 2, in <module>
    raise TypeError('Something awful has happened')
TypeError: Something awful has happened

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/user/tmp.py", line 4, in <module>
    raise ValueError('There was a bad value') from e
ValueError: There was a bad value

Though the result may seem similar, in fact it is rather different! raise from saves the context of the original exception and allows one to trace all the exceptions chain back - which is impossible with simple raise.

To get the original exception, you simply have to refer to new exception's __context__ attribute, i.e.

try:
    try:
        raise TypeError('Something awful has happened')
    except TypeError as e:
        raise ValueError('There was a bad value') from e
except ValueError as e:
    print(e.__context__)
>>> Something awful has happened

Hopefully that is the solution you were looking for.

For more details, see PEP 3134 -- Exception Chaining and Embedded Tracebacks

like image 120
Zaur Nasibov Avatar answered Sep 23 '22 06:09

Zaur Nasibov