3

Is there a way in Python to raise an error that has another error as its cause?

In Java, you can create an instance of an exception with a cause such as in the following code

try {
    throw new IOException();
} catch (IOException e) {
    throw new RuntimeException("An exception occurred while trying to execute", e);
}

resulting in in this error message:

Exception in thread "main" java.lang.RuntimeException: An exception occurred while trying to execute
    at thing.Main.main(Main.java:11)
Caused by: java.io.IOException
    at thing.Main.main(Main.java:9)

Notice that the first exception (in the stack trace) is "caused by" the second.

This is, in my opinion, a great way to show an API user that a higher-level error occurred during a call, and the developer can debug it by looking at the lower-level exception which is the "cause" of the higher-level error (in this case the RuntimeException is caused by the IOException).

With the searches I've made, I haven't been able to find anything about having an error as the cause of another in Python. Can this be achieved in Python? How? And if not, what would be a Pythonic equivalent?

Zakru
  • 92
  • 1
  • 11

1 Answers1

4

In Python it is achieved by a very similar structure:

try:
    raise ValueError
except ValueError:
    raise ValueError('second exception')

This will generate the following traceback:

Traceback (most recent call last):
  File "main.py", line 2, in <module>
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "main.py", line 4, in <module>
    raise ValueError('second exception')
ValueError: second exception

Another Python feature is raise from which provide a slightly different traceback:

try:
    raise ValueError
except ValueError as e:
    raise ValueError('second exception') from e

Traceback:

Traceback (most recent call last):
  File "main.py", line 2, in <module>
    raise ValueError
ValueError

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

Traceback (most recent call last):
  File "main.py", line 4, in <module>
    raise ValueError('second exception') from e
ValueError: second exception

Further reading:

DeepSpace
  • 78,697
  • 11
  • 109
  • 154
  • "Another Python feature is raise from which provide a slightly different traceback:" the only difference is that it's printing different code where the code is different. `from ` is implicit to `raise `. The `from` clause is useful to either link to some other exception or to suppress exception chaining (with `from None`). – Masklinn Jan 14 '20 at 11:15
  • So basically `raise from` clarifies in the traceback that the first exception was an expected possibility, and that the second error tells (or at least should tell) what the user did wrong on their part? – Zakru Jan 14 '20 at 11:16
  • 1
    @Masklinn the first traceback states "During handling of the above exception, another exception occurred", while the second states "The above exception was the direct cause of the following exception". Which makes it clear that, in the second case, the second exception was intentionnaly raised as a result of the first, while in the first case it could be an unexpected exception. – bruno desthuilliers Jan 14 '20 at 12:27