503

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
6

4 Answers 4

543

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 "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
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 "<stdin>", line 2, in <module>
ZeroDivisionError: int division or modulo by zero
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

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

19
  • 38
    Is there any reason to explicitly chain the exceptions using from and __cause__ in lieu of the implicit __context__? Are there any cases where one would attach a different exception than the one caught by the except?
    – darkfeline
    Commented Jul 16, 2014 at 2:45
  • 37
    @darkfeline: Lets say your database API supports opening databases from various sources, including the web and disk. Your API will always raise a DatabaseError if opening the database fails. But if the failure is the result of a IOError because a file failed to open or a HTTPError because a URL failed to work then that is context you want to include explicitly, so the developer using the API can debug why this is. At that moment you use raise DatabaseError from original_exception. Commented Jul 16, 2014 at 8:00
  • 8
    @darkfeline: If that developer is wrapping the use of the database API in their own API and wanted to pass on that IOError or HTTPError on to their consumers, then they'd have to use raise NewException from databaseexception.__cause__, now using a different exception from the DatabaseException that they just caught. Commented Jul 16, 2014 at 8:03
  • 3
    @dan3: no, there isn't. Exception chaining is purely a Python 3 feature. Commented Apr 9, 2015 at 20:35
  • 13
    @laike9m: you mean when you are handling exception foo, and want to raise a new exception bar? Then you can use raise bar from foo and have Python state that foo directly caused bar. If you don't use from foo, then Python will still print both, but state that during handling foo, bar was raised, Different message, intended to flag a possible bug in the error handling. Commented Aug 14, 2016 at 9:05
25

In 2005, PEP 3134, Exception Chaining and Embedded Tracebacks introduced exception chaining:

  • implicit chaining with explicit raise EXCEPTION or implicit raise (__context__ attribute);
  • explicit chaining with explicit raise EXCEPTION from CAUSE (__cause__ attribute).

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:

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:

raise EXCEPTION from CAUSE

which is equivalent to:

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.

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:

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)

[…]

In 2012, PEP 415, Implement Context Suppression with Exception Attributes introduced exception context suppression with explicit raise EXCEPTION from None (__suppress_context__ attribute).

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:

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 for the default exception handler (its job is to report exceptions) 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)
6

The shortest answer. PEP-3134 says it all. raise Exception from e sets the __cause__ filed of the new exception.

A longer answer from the same PEP:

  • __context__ field would be set implicitly to the original error inside except: block unless told not to with __suppress_context__ = True.
  • __cause__ is just like context but has to be set explicitly by using from syntax
  • traceback will always chain when you call raise inside an except block. You can get rid of traceback by a) swallowing an exception except: pass or by messing with sys.exc_info() directly.

The long answer

import traceback 
import sys
class CustomError(Exception):
    def __init__(self):
        super().__init__("custom")
def print_exception(func):
    print(f"\n\n\nEXECURTING FUNCTION '{func.__name__}' \n")
    try:
        func()
    except Exception as e:
        "Here is result of our actions:"
        print(f"\tException type:    '{type(e)}'")
        print(f"\tException message: '{e}'")
        print(f"\tException context: '{e.__context__}'")
        print(f"\tContext type:      '{type(e.__context__)}'")
        print(f"\tException cause:   '{e.__cause__}'")
        print(f"\tCause type:         '{type(e.__cause__)}'")
        print("\nTRACEBACKSTART>>>")
        traceback.print_exc()
        print("<<<TRACEBACKEND")
def original_error_emitter():
    x = {}
    print(x.does_not_exist)
def vanilla_catch_swallow():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        pass
def vanilla_catch_reraise():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        raise e
def catch_replace():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        raise CustomError()
def catch_replace_with_from():
    """Nothing is expected to happen"""
    try:
        original_error_emitter()
    except Exception as e:
        raise CustomError() from e
def catch_reset_trace():
    saw_an_error = False
    try:
        original_error_emitter()
    except Exception as e:
        saw_an_error = True
    if saw_an_error:
        raise CustomError()
print("Note: This will print nothing")
print_exception(vanilla_catch_swallow)
print("Note: This will print AttributeError and 1 stack trace")
print_exception(vanilla_catch_reraise)
print("Note: This will print CustomError with no context but 2 stack traces")
print_exception(catch_replace)
print("Note: This will print CustomError with AttributeError context and 2 stack traces")
print_exception(catch_replace_with_from)
print("Note: This will brake traceback chain")
print_exception(catch_reset_trace)

Will result in the following output:

Note: This will print nothing
EXECURTING FUNCTION 'vanilla_catch_swallow' 
Note: This will print AttributeError and 1 stack trace
EXECURTING FUNCTION 'vanilla_catch_reraise' 
        Exception type:    '<class 'AttributeError'>'
        Exception message: ''dict' object has no attribute 'does_not_exist''
        Exception context: 'None'
        Context type:      '<class 'NoneType'>'
        Exception cause:   'None'
        Cause type:         '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 41, in vanilla_catch_reraise
    raise e
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 39, in vanilla_catch_reraise
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
<<<TRACEBACKEND
Note: This will print CustomError with no context but 2 stack traces
EXECURTING FUNCTION 'catch_replace' 
        Exception type:    '<class '__main__.CustomError'>'
        Exception message: 'custom'
        Exception context: ''dict' object has no attribute 'does_not_exist''
        Context type:      '<class 'AttributeError'>'
        Exception cause:   'None'
        Cause type:         '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 46, in catch_replace
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 48, in catch_replace
    raise CustomError()
CustomError: custom
<<<TRACEBACKEND
Note: This will print CustomError with AttributeError context and 2 stack traces
EXECURTING FUNCTION 'catch_replace_with_from' 
        Exception type:    '<class '__main__.CustomError'>'
        Exception message: 'custom'
        Exception context: ''dict' object has no attribute 'does_not_exist''
        Context type:      '<class 'AttributeError'>'
        Exception cause:   ''dict' object has no attribute 'does_not_exist''
        Cause type:         '<class 'AttributeError'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 53, in catch_replace_with_from
    original_error_emitter()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 27, in original_error_emitter
    print(x.does_not_exist)
AttributeError: 'dict' object has no attribute 'does_not_exist'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 55, in catch_replace_with_from
    raise CustomError() from e
CustomError: custom
<<<TRACEBACKEND
Note: This will brake traceback chain
EXECURTING FUNCTION 'catch_reset_trace' 
        Exception type:    '<class '__main__.CustomError'>'
        Exception message: 'custom'
        Exception context: 'None'
        Context type:      '<class 'NoneType'>'
        Exception cause:   'None'
        Cause type:         '<class 'NoneType'>'
TRACEBACKSTART>>>
Traceback (most recent call last):
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 11, in print_exception
    func()
  File "/Users/eugene.selivonchyk/repo/experiments/exceptions.py", line 64, in catch_reset_trace
    raise CustomError()
CustomError: custom
<<<TRACEBACKEND
5

Preamble

This is technically explained in the documentation. But it's very confusing and lacks examples. Here's some examples that I think explain the situation much more clearly than the __context__, __cause__, __supress_context__ funny business.

The main examples

import sys
from traceback import print_exc
class ExceptionA(Exception): pass
class ExceptionB(Exception): pass
print('## Naked exception ##')
try:
    raise ExceptionA
except:
    print_exc(file=sys.stdout)
print('\n\n\n## except and raise ##')
try:
    try:
        raise ExceptionA
    except ExceptionA:
        raise ExceptionB
except:
    print_exc(file=sys.stdout)
print('\n\n\n## except and raise from e ##')
try:
    try:
        raise ExceptionA
    except ExceptionA as e:
        raise ExceptionB from e
except:
    print_exc(file=sys.stdout)
print('\n\n\n## except and raise from None ##')
try:
    try:
        raise ExceptionA
    except ExceptionA:
        raise ExceptionB from None
except:
    print_exc(file=sys.stdout)

yields

## Naked exception ##
Traceback (most recent call last):
  File "...\raise.py", line 9, in <module>
    raise ExceptionA
ExceptionA
## except and raise ##
Traceback (most recent call last):
  File "...\raise.py", line 16, in <module>
    raise ExceptionA
ExceptionA
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "...\raise.py", line 18, in <module>
    raise ExceptionB
ExceptionB
## except and raise from e ##
Traceback (most recent call last):
  File "...\raise.py", line 25, in <module>
    raise ExceptionA
ExceptionA
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "...\raise.py", line 27, in <module>
    raise ExceptionB from e
ExceptionB
## except and raise from None ##
Traceback (most recent call last):
  File "...\raise.py", line 36, in <module>
    raise ExceptionB from None
ExceptionB

So we get 4 behaviors.

  • With no exception "handling" we just print out the ExceptionA.
  • When a new exception B is directly raised during the handling of exception A we see tracebacks for A and B concatenated by the message "During handling of the above exception, another exception occurred:"
  • When a new exception B is raised from A during the handling of A we see tracebacks for A and B concatenated by the message "The above exception was the direct cause of the following exception:"
  • When a new exception B is raised from None during the handling of A wee only see the traceback for B.

Why raise from e?

I struggle to see the practical difference between options 2 and 3. The only difference is the message between the concatenated tracebacks, so the difference purely has to do with human readers. So the two have different semantics. From what I can gather, the semantics of the second option (raise ExceptionB) say: We were handling exception A and then another unexpected exception B happened. The third option (raise ExceptionB from e) says that we are sort of transforming ExceptionB from an internal exception into a possibly more informative user-facing exception. I think a common use case would be something like

from traceback import print_exc
import sys
my_dict = {'a': 1}
print('## No handling ##')
try:
    print(my_dict['b'])
except:
    print_exc(file=sys.stdout)
print('\n\n\n## Handling the exception to add more info for the reader ##')
try:
    try:
        print(my_dict['b'])
    except KeyError as e:
        raise KeyError('You forgot to include a key in your dict!') from e
except:
    print_exc(file=sys.stdout)

yields

## No handling ##
Traceback (most recent call last):
  File "...\raise_2.py", line 9, in <module>
    print(my_dict['b'])
KeyError: 'b'
## Handling the exception to add more info for the reader ##
Traceback (most recent call last):
  File "...\raise_2.py", line 16, in <module>
    print(my_dict['b'])
KeyError: 'b'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "...\raise_2.py", line 18, in <module>
    raise KeyError('You forgot to include a key in your dict!') from e
KeyError: 'You forgot to include a key in your dict!'

The point here is we don't want the traceback to just point out that the second key error "occurred during the handling of the first key error". It's more helpful to point out that the second key error occurred BECAUSE the first key error occurred. That is, the have the same cause (hence __cause__). So in this sense the developer can communicate a little bit more information to whoever is reading the traceback. However, in some cases you may want to suppress the underlying cause entirely and provide the user a shorter more descriptive traceback. In this case you use raise ... from None.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.