Python "raise from" usage
Question:
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
Answers:
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.
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)
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"nnnEXECURTING 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
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
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.
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 aZeroDivisionError
. Thecompute()
function catches this exception and callslog(exc)
, but thelog()
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 anIOError
. TheZeroDivisionError
is lost. With the proposed change, the instance ofIOError
has an additional__context__
attribute that retains theZeroDivisionError
.[…]
Explicit Exception Chaining
The
__cause__
attribute on exception objects is always initialized toNone
. It is set by a new form of theraise
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 aDatabaseError
, with a__cause__
attribute that reveals theIOError
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 toTrue
. In particular,raise exc from cause
syntax will setexc.__suppress_context__
toTrue
. 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 theprint_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 setsexc.__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)
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 insideexcept:
block unless told not to with__suppress_context__ = True
.__cause__
is just like context but has to be set explicitly by usingfrom
syntaxtraceback
will always chain when you callraise
inside anexcept
block. You can get rid of traceback by a) swallowing an exceptionexcept: pass
or by messing withsys.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"nnnEXECURTING 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