Python exception chaining
Question:
Is there a standard way of using exception chains in Python? Like the Java exception ’caused by’?
Here is some background.
I have a module with one main exception class DSError
:
class DSError(Exception):
pass
Somewhere within this module there will be:
try:
v = my_dict[k]
something(v)
except KeyError as e:
raise DSError("no key %s found for %s" % (k, self))
except ValueError as e:
raise DSError("Bad Value %s found for %s" % (v, self))
except DSError as e:
raise DSError("%s raised in %s" % (e, self))
Basically this snippet should throw only DSError and tell me what happened and why. The thing is that the try block might throw lots of other exceptions, so I’d prefer if I can do something like:
try:
v = my_dict[k]
something(v)
except Exception as e:
raise DSError(self, v, e) # Exception chained...
Is this standard pythonic way? I did not see exception chains in other modules so how is that done in Python?
Answers:
Is this what you’re asking for?
class MyError(Exception):
def __init__(self, other):
super(MyError, self).__init__(other.message)
>>> try:
... 1/0
... except Exception, e:
... raise MyError(e)
Traceback (most recent call last):
File "<pyshell#27>", line 4, in <module>
raise MyError(e)
MyError: division by zero
If you want to store the original exception object, you can certainly do so in your own exception class’s __init__
. You might actually want to store the traceback as the exception object itself doesn’t provide much useful information about where the exception occurred:
class MyError(Exception):
def __init__(self, other):
self.traceback = sys.exc_info()
super(MyError, self).__init__(other.message)
After this you can access the traceback
attribute of your exception to get info about the original exception. (Python 3 already provides this as the __traceback__
attribute of an exception object.)
Exception chaining is only available in Python 3, where you can write:
try:
v = {}['a']
except KeyError as e:
raise ValueError('failed') from e
which yields an output like
Traceback (most recent call last):
File "t.py", line 2, in <module>
v = {}['a']
KeyError: 'a'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "t.py", line 4, in <module>
raise ValueError('failed') from e
ValueError: failed
In most cases, you don’t even need the from
; Python 3 will by default show all exceptions that occured during exception handling, like this:
Traceback (most recent call last):
File "t.py", line 2, in <module>
v = {}['a']
KeyError: 'a'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "t.py", line 4, in <module>
raise ValueError('failed')
ValueError: failed
What you can do in Python 2 is adding custom attributes to your exception class, like:
class MyError(Exception):
def __init__(self, message, cause):
super(MyError, self).__init__(message + u', caused by ' + repr(cause))
self.cause = cause
try:
v = {}['a']
except KeyError as e:
raise MyError('failed', e)
Is there a standard way of using exception chains in Python? Like the Java exception ’caused by’?
Here is some background.
I have a module with one main exception class DSError
:
class DSError(Exception):
pass
Somewhere within this module there will be:
try:
v = my_dict[k]
something(v)
except KeyError as e:
raise DSError("no key %s found for %s" % (k, self))
except ValueError as e:
raise DSError("Bad Value %s found for %s" % (v, self))
except DSError as e:
raise DSError("%s raised in %s" % (e, self))
Basically this snippet should throw only DSError and tell me what happened and why. The thing is that the try block might throw lots of other exceptions, so I’d prefer if I can do something like:
try:
v = my_dict[k]
something(v)
except Exception as e:
raise DSError(self, v, e) # Exception chained...
Is this standard pythonic way? I did not see exception chains in other modules so how is that done in Python?
Is this what you’re asking for?
class MyError(Exception):
def __init__(self, other):
super(MyError, self).__init__(other.message)
>>> try:
... 1/0
... except Exception, e:
... raise MyError(e)
Traceback (most recent call last):
File "<pyshell#27>", line 4, in <module>
raise MyError(e)
MyError: division by zero
If you want to store the original exception object, you can certainly do so in your own exception class’s __init__
. You might actually want to store the traceback as the exception object itself doesn’t provide much useful information about where the exception occurred:
class MyError(Exception):
def __init__(self, other):
self.traceback = sys.exc_info()
super(MyError, self).__init__(other.message)
After this you can access the traceback
attribute of your exception to get info about the original exception. (Python 3 already provides this as the __traceback__
attribute of an exception object.)
Exception chaining is only available in Python 3, where you can write:
try:
v = {}['a']
except KeyError as e:
raise ValueError('failed') from e
which yields an output like
Traceback (most recent call last):
File "t.py", line 2, in <module>
v = {}['a']
KeyError: 'a'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "t.py", line 4, in <module>
raise ValueError('failed') from e
ValueError: failed
In most cases, you don’t even need the from
; Python 3 will by default show all exceptions that occured during exception handling, like this:
Traceback (most recent call last):
File "t.py", line 2, in <module>
v = {}['a']
KeyError: 'a'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "t.py", line 4, in <module>
raise ValueError('failed')
ValueError: failed
What you can do in Python 2 is adding custom attributes to your exception class, like:
class MyError(Exception):
def __init__(self, message, cause):
super(MyError, self).__init__(message + u', caused by ' + repr(cause))
self.cause = cause
try:
v = {}['a']
except KeyError as e:
raise MyError('failed', e)