Python "raise from" usage

PythonPython 3.xSyntaxException Handling

Python Problem Overview


What's the difference between raise and raise from in Python?

try:
    raise ValueError
except Exception as e:
    raise IndexError

which yields

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

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "tmp.py", line 4, in <module>
    raise IndexError
IndexError

and

try:
    raise ValueError
except Exception as e:
    raise IndexError from e

which yields

Traceback (most recent call last):
  File "tmp.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 "tmp.py", line 4, in <module>
    raise IndexError from e
IndexError

Python Solutions


Solution 1 - Python

The difference is that when you use from, the __cause__ attribute is set and the message states that the exception was directly caused by. If you omit the from then no __cause__ is set, but the __context__ attribute may be set as well, and the traceback then shows the context as during handling something else happened.

Setting the __context__ happens if you used raise in an exception handler; if you used raise anywhere else no __context__ is set either.

If a __cause__ is set, a __suppress_context__ = True flag is also set on the exception; when __suppress_context__ is set to True, the __context__ is ignored when printing a traceback.

When raising from a exception handler where you don't want to show the context (don't want a during handling another exception happened message), then use raise ... from None to set __suppress_context__ to True.

In other words, Python sets a context on exceptions so you can introspect where an exception was raised, letting you see if another exception was replaced by it. You can also add a cause to an exception, making the traceback explicit about the other exception (use different wording), and the context is ignored (but can still be introspected when debugging). Using raise ... from None lets you suppress the context being printed.

See the raise statement documenation:

> 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: > > >>> try: > ... print(1 / 0) > ... except Exception as exc: > ... raise RuntimeError("Something bad happened") from exc > ... > Traceback (most recent call last): > File "", line 2, in > ZeroDivisionError: int division or modulo by zero > > The above exception was the direct cause of the following exception: > > Traceback (most recent call last): > File "", line 4, in > RuntimeError: Something bad happened > > A similar mechanism works implicitly if an exception is raised inside an exception handler or a finally clause: the previous exception is then attached as the new exception’s __context__ attribute: > > >>> try: > ... print(1 / 0) > ... except: > ... raise RuntimeError("Something bad happened") > ... > Traceback (most recent call last): > File "", line 2, in > ZeroDivisionError: int division or modulo by zero >
> During handling of the above exception, another exception occurred: >
> Traceback (most recent call last): > File "", line 4, in > RuntimeError: Something bad happened

Also see the Built-in Exceptions documentation for details on the context and cause information attached to exceptions.

Solution 2 - Python

PEP 3134, Exception Chaining and Embedded Tracebacks introduced chaining of exceptions (implicitly chained with explicit raise EXCEPTION or implicit raise, and explicitly chained with explicit raise EXCEPTION from CAUSE). Here are the relevant paragraphs to understand their usage:

> Motivation > ---------- > > During the handling of one exception (exception A), it is possible that another exception (exception B) may occur. In today’s Python (version 2.4), if this happens, exception B is propagated outward and exception A is lost. In order to debug the problem, it is useful to know about both exceptions. The __context__ attribute retains this information automatically. > > Sometimes it can be useful for an exception handler to intentionally re-raise an exception, either to provide extra information or to translate an exception to another type. The __cause__ attribute provides an explicit way to record the direct cause of an exception. > > […] > > Implicit Exception Chaining > --------------------------- > > Here is an example to illustrate the __context__ attribute: > > python > def compute(a, b): > try: > a/b > except Exception, exc: > log(exc) > > def log(exc): > file = open('logfile.txt') # oops, forgot the 'w' > print >>file, exc > file.close() > > > Calling compute(0, 0) causes a ZeroDivisionError. The compute() function catches this exception and calls log(exc), but the log() function also raises an exception when it tries to write to a file that wasn’t opened for writing. > > In today’s Python, the caller of compute() gets thrown an IOError. The ZeroDivisionError is lost. With the proposed change, the instance of IOError has an additional __context__ attribute that retains the ZeroDivisionError. > > […] > > Explicit Exception Chaining > --------------------------- > > The __cause__ attribute on exception objects is always initialized to None. It is set by a new form of the raise statement: > > python > raise EXCEPTION from CAUSE > > > which is equivalent to: > > python > exc = EXCEPTION > exc.__cause__ = CAUSE > raise exc > > > In the following example, a database provides implementations for a few different kinds of storage, with file storage as one kind. The database designer wants errors to propagate as DatabaseError objects so that the client doesn’t have to be aware of the storage-specific details, but doesn’t want to lose the underlying error information. > > python > class DatabaseError(Exception): > pass > > class FileDatabase(Database): > def __init__(self, filename): > try: > self.file = open(filename) > except IOError, exc: > raise DatabaseError('failed to open') from exc > > > If the call to open() raises an exception, the problem will be reported as a DatabaseError, with a __cause__ attribute that reveals the IOError as the original cause. > > Enhanced Reporting > ------------------ > > The default exception handler will be modified to report chained exceptions. The chain of exceptions is traversed by following the __cause__ and __context__ attributes, with __cause__ taking priority. In keeping with the chronological order of tracebacks, the most recently raised exception is displayed last; that is, the display begins with the description of the innermost exception and backs up the chain to the outermost exception. The tracebacks are formatted as usual, with one of the lines: > > > The above exception was the direct cause of the following exception: > > or > > > During handling of the above exception, another exception occurred: > > between tracebacks, depending whether they are linked by __cause__ or __context__ respectively. Here is a sketch of the procedure: > > python > def print_chain(exc): > if exc.__cause__: > print_chain(exc.__cause__) > print '\nThe above exception was the direct cause...' > elif exc.__context__: > print_chain(exc.__context__) > print '\nDuring handling of the above exception, ...' > print_exc(exc) > > > […]

PEP 415, Implement Context Suppression with Exception Attributes then introduced suppression of exception contexts (with explicit raise EXCEPTION from None). Here is the relevant paragraph to understand its usage:

> Proposal > -------- > > A new attribute on BaseException, __suppress_context__, will be introduced. Whenever __cause__ is set, __suppress_context__ will be set to True. In particular, raise exc from cause syntax will set exc.__suppress_context__ to True. Exception printing code will check for that attribute to determine whether context and cause will be printed. __cause__ will return to its original purpose and values. > > There is precedence for __suppress_context__ with the print_line_and_file exception attribute. > > To summarize, raise exc from cause will be equivalent to: > > python > exc.__cause__ = cause > raise exc > > > where exc.__cause__ = cause implicitly sets exc.__suppress_context__.

So in PEP 415, the sketch of the procedure given in PEP 3134 becomes the following:

def print_chain(exc):
    if exc.__cause__:
        print_chain(exc.__cause__)
        print '\nThe above exception was the direct cause...'
    elif exc.__context__ and not exc.__suppress_context__:
        print_chain(exc.__context__)
        print '\nDuring handling of the above exception, ...'
    print_exc(exc)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondarkfelineView Question on Stackoverflow
Solution 1 - PythonMartijn PietersView Answer on Stackoverflow
Solution 2 - PythonMaggyeroView Answer on Stackoverflow