How do I raise an exception in Python so that it can later be caught via an except block?
How do I manually throw/raise an exception in Python?
Use the most specific Exception constructor that semantically fits your issue.
Be specific in your message, e.g.:
raise ValueError('A very specific bad thing happened.')
Don't raise generic exceptions
Avoid raising a generic Exception. To catch it, you'll have to catch all other more specific exceptions that subclass it.
Problem 1: Hiding bugs
raise Exception('I know Python!') # Don't! If you catch, likely to hide bugs.
For example:
def demo_bad_catch():
try:
raise ValueError('Represents a hidden bug, do not catch this')
raise Exception('This is the exception you expect to handle')
except Exception as error:
print('Caught this error: ' + repr(error))
>>> demo_bad_catch()
Caught this error: ValueError('Represents a hidden bug, do not catch this',)
Problem 2: Won't catch
And more specific catches won't catch the general exception:
def demo_no_catch():
try:
raise Exception('general exceptions not caught by specific handling')
except ValueError as e:
print('we will not catch exception: Exception')
>>> demo_no_catch()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in demo_no_catch
Exception: general exceptions not caught by specific handling
Best Practices: raise statement
Instead, use the most specific Exception constructor that semantically fits your issue.
raise ValueError('A very specific bad thing happened')
which also handily allows an arbitrary number of arguments to be passed to the constructor:
raise ValueError('A very specific bad thing happened', 'foo', 'bar', 'baz')
These arguments are accessed by the args attribute on the Exception object. For example:
try:
some_code_that_may_raise_our_value_error()
except ValueError as err:
print(err.args)
prints
('message', 'foo', 'bar', 'baz')
In Python 2.5, an actual message attribute was added to BaseException in favor of encouraging users to subclass Exceptions and stop using args, but the introduction of message and the original deprecation of args has been retracted.
Best Practices: except clause
When inside an except clause, you might want to, for example, log that a specific type of error happened, and then re-raise. The best way to do this while preserving the stack trace is to use a bare raise statement. For example:
logger = logging.getLogger(__name__)
try:
do_something_in_app_that_breaks_easily()
except AppError as error:
logger.error(error)
raise # just this!
# raise AppError # Don't do this, you'll lose the stack trace!
Don't modify your errors... but if you insist.
You can preserve the stacktrace (and error value) with sys.exc_info(), but this is way more error prone and has compatibility problems between Python 2 and 3, prefer to use a bare raise to re-raise.
To explain - the sys.exc_info() returns the type, value, and traceback.
type, value, traceback = sys.exc_info()
This is the syntax in Python 2 - note this is not compatible with Python 3:
raise AppError, error, sys.exc_info()[2] # avoid this.
# Equivalently, as error *is* the second object:
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
If you want to, you can modify what happens with your new raise - e.g. setting new args for the instance:
def error():
raise ValueError('oops!')
def catch_error_modify_message():
try:
error()
except ValueError:
error_type, error_instance, traceback = sys.exc_info()
error_instance.args = (error_instance.args[0] + ' <modification>',)
raise error_type, error_instance, traceback
And we have preserved the whole traceback while modifying the args. Note that this is not a best practice and it is invalid syntax in Python 3 (making keeping compatibility much harder to work around).
>>> catch_error_modify_message()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in catch_error_modify_message
File "<stdin>", line 2, in error
ValueError: oops! <modification>
In Python 3:
raise error.with_traceback(sys.exc_info()[2])
Again: avoid manually manipulating tracebacks. It's less efficient and more error prone. And if you're using threading and sys.exc_info you may even get the wrong traceback (especially if you're using exception handling for control flow - which I'd personally tend to avoid.)
Python 3, Exception chaining
In Python 3, you can chain Exceptions, which preserve tracebacks:
raise RuntimeError('specific message') from error
Be aware:
this does allow changing the error type raised, and
this is not compatible with Python 2.
Deprecated Methods:
These can easily hide and even get into production code. You want to raise an exception, and doing them will raise an exception, but not the one intended!
Valid in Python 2, but not in Python 3 is the following:
raise ValueError, 'message' # Don't do this, it's deprecated!
Only valid in much older versions of Python (2.4 and lower), you may still see people raising strings:
raise 'message' # really really wrong. don't do this.
In all modern versions, this will actually raise a TypeError, because you're not raising a BaseException type. If you're not checking for the right exception and don't have a reviewer that's aware of the issue, it could get into production.
Example Usage
I raise Exceptions to warn consumers of my API if they're using it incorrectly:
def api_func(foo):
'''foo should be either 'baz' or 'bar'. returns something very useful.'''
if foo not in _ALLOWED_ARGS:
raise ValueError('{foo} wrong, use "baz" or "bar"'.format(foo=repr(foo)))
Create your own error types when apropos
"I want to make an error on purpose, so that it would go into the except"
You can create your own error types, if you want to indicate something specific is wrong with your application, just subclass the appropriate point in the exception hierarchy:
class MyAppLookupError(LookupError):
'''raise this when there's a lookup error for my app'''
and usage:
if important_key not in resource_dict and not ok_to_be_missing:
raise MyAppLookupError('resource is missing, and that is not ok.')
Don't do this. Raising a bare Exception is absolutely not the right thing to do; see Aaron Hall's excellent answer instead.
It can't get much more Pythonic than this:
raise Exception("I know Python!")
Replace Exception with the specific type of exception you want to throw.
See the raise statement documentation for Python if you'd like more information.
In Python 3 there are four different syntaxes for raising exceptions:
raise exception
raise exception (args)
raise
raise exception (args) from original_exception
1. Raise exception vs. 2. raise exception (args)
If you use raise exception (args) to raise an exception then the args will be printed when you print the exception object - as shown in the example below.
# Raise exception (args)
try:
raise ValueError("I have raised an Exception")
except ValueError as exp:
print ("Error", exp) # Output -> Error I have raised an Exception
# Raise exception
try:
raise ValueError
except ValueError as exp:
print ("Error", exp) # Output -> Error
3. Statement raise
The raise statement without any arguments re-raises the last exception.
This is useful if you need to perform some actions after catching the exception and then want to re-raise it. But if there wasn't any exception before, the raise statement raises a TypeError Exception.
def somefunction():
print("some cleaning")
a=10
b=0
result=None
try:
result=a/b
print(result)
except Exception: # Output ->
somefunction() # Some cleaning
raise # Traceback (most recent call last):
# File "python", line 8, in <module>
# ZeroDivisionError: division by zero
4. Raise exception (args) from original_exception
This statement is used to create exception chaining in which an exception that is raised in response to another exception can contain the details of the original exception - as shown in the example below.
class MyCustomException(Exception):
pass
a=10
b=0
reuslt=None
try:
try:
result=a/b
except ZeroDivisionError as exp:
print("ZeroDivisionError -- ",exp)
raise MyCustomException("Zero Division ") from exp
except MyCustomException as exp:
print("MyException",exp)
print(exp.__cause__)
Output:
ZeroDivisionError -- division by zero
MyException Zero Division
division by zero
For the common case where you need to throw an exception in response to some unexpected conditions, and that you never intend to catch, but simply to fail fast to enable you to debug from there if it ever happens — the most logical one seems to be AssertionError:
if 0 < distance <= RADIUS:
#Do something.
elif RADIUS < distance:
#Do something.
else:
raise AssertionError("Unexpected value of 'distance'!", distance)
Read the existing answers first, this is just an addendum.
Notice that you can raise exceptions with or without arguments.
Example:
raise SystemExit
exits the program, but you might want to know what happened. So you can use this.
raise SystemExit("program exited")
This will print "program exited" to standard error before closing the program.
Just to note: there are times when you do want to handle generic exceptions. If you're processing a bunch of files and logging your errors, you might want to catch any error that occurs for a file, log it, and continue processing the rest of the files. In that case, a
try:
foo()
except Exception as e:
print(e) # Print out handled error
block is a good way to do it. You'll still want to raise specific exceptions so you know what they mean, though.
Another way to throw an exception is using assert. You can use assert to verify a condition is being fulfilled. If not, then it will raise AssertionError. For more details have a look here.
def avg(marks):
assert len(marks) != 0, "List is empty."
return sum(marks)/len(marks)
mark2 = [55,88,78,90,79]
print("Average of mark2:", avg(mark2))
mark1 = []
print("Average of mark1:", avg(mark1))
You might also want to raise custom exceptions. For example, if you're writing a library, it's a very good practice to make a base exception class for your module, and then have custom sub-exceptions to be more specific.
You can achieve that like this:
class MyModuleBaseClass(Exception):
pass
class MoreSpecificException(MyModuleBaseClass):
pass
# To raise custom exceptions, you can just
# use the raise keyword
raise MoreSpecificException
raise MoreSpecificException('message')
If you're not interested in having a custom base class, you can just inherit your custom exception classes from an ordinary exception class like Exception, TypeError, ValueError, etc.
If you don't care about which error to raise, you could use assert to raise an AssertionError:
>>> assert False, "Manually raised error"
Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
assert False, "Manually raised error"
AssertionError: Manually raised error
>>>
The assert keyword raises an AssertionError if the condition is False. In this case, we specified False directly, so it raises the error, but to have it have a text we want it to raise to, we add a comma and specify the error text we want. In this case, I wrote Manually raised error and this raises it with that text.
You should learn the raise statement of Python for that.
It should be kept inside the try block.
Example -
try:
raise TypeError # Replace TypeError by any other error if you want
except TypeError:
print('TypeError raised')
Related
I expect a python function I use to raise a KeyError, in case the user of my script entered a wrong value.
When this happens, I want to raise a ValueError, not a KeyError. I tried to catch the KeyError, then raise a ValueError instead. I then get both.
For example:
def func(my_dict):
return my_dict["my_key"]
a = {}
try:
func(a)
except KeyError as e:
raise ValueError("Replaced exception")
results in this:
$ python3 except.py
Traceback (most recent call last):
File "except.py", line 7, in <module>
func(a)
File "except.py", line 2, in func
return my_dict["my_key"]
KeyError: 'my_key'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "except.py", line 9, in <module>
raise ValueError("Replaced exception")
ValueError: Replaced exception
I don't care about the KeyError itself, I just want to tell the user about the ValueError.
How can I catch the KeyError, but discard it? Is it bad practice? How should I organize this instead?
One way, described here, is to raise ... from None
def func(my_dict):
return my_dict["my_key"]
a = {}
try:
func(a)
except KeyError as e:
raise ValueError("Replaced exception") from None
But I'm still not sure if this is a good idea or not.
What you stumbled upon is the difference between an exceptions cause and it's context. There are three slightly different things you can do, and each have different semantics:
Version 1:
try:
raise KeyError()
except KeyError as ex:
raise ValueError()
This will show both trackbacks separated by the line
During handling of the above exception, another exception occurred:
This is saying: There was one exception, and in the catch block there was another (unrelated, usually unexpected) exception. This is not what you want here.
Version 2:
try:
raise KeyError()
except KeyError as ex:
raise ValueError() from None
This will just show the traceback for the ValueError. This might be what you want, if you are really sure you never ever have to debug the KeyError. Otherwise, read on.
Version 3
try:
raise KeyError()
except KeyError as ex:
raise ValueError() from ex
Again, this will show both tracebacks, this time separated by the line
The above exception was the direct cause of the following exception:
Notice the difference from Version 1? Semantically this says: On this level of abstraction this is a ValueError. But in case you need to debug it, here's the traceback of the original KeyError, too. Pretty neat, don't you think?
You don't have a try/except block to manage the ValueError. So, what you're seeing is a stack trace that's telling you where the ValueError originated.
Consider this:
def func(my_dict):
return my_dict["my_key"]
a = {}
try:
try:
func(a)
except KeyError as e:
raise ValueError("Replaced exception")
except ValueError as e:
print(e)
Output:
Replaced exception
i.e.,
No stack trace because you've caught the ValueError
See the following code
def ex_string(ex):
return "Error:{} Traceback:{}".format(ex, traceback.format_exc())
def map_values(listing):
...
obj = listing['value']
...
def process_listing(listing):
try:
listing = map_values(listing)
except Exception as ex:
do_some_handling(listing)
print ex_string(ex) # this stack shows actual line inside map_values
raise ex
def start():
for listing in listings:
try:
process_listing(listing)
save_listing(listing)
except Exception as ex:
error = ex_string(ex) #this stack shows map_values function only
log_error(error)
As above, when listing does not contain 'value' element, I don't know which line is causing issue, all I know from log is somewhere inside map_values function, how do I get the full stack trace from where the exception was first raised? In this case obj = listing['value']
Use raise with no argument inside an except clause to reraise the exception you're handling with its original traceback:
def process_listing(listing):
try:
listing = map_values(listing)
except Exception as ex:
do_some_handling(listing)
print ex_string(ex)
raise # no ex
raise with no argument means you want the exception you're handling to be considered unhandled, so it resumes propagating with the same traceback.
Using raise with one argument here means you're signalling that your exception handler has encountered an error, so Python constructs a new stack trace pointing to the raise in the exception handler.
Note that raise with an argument behaves differently on Python 3, where exceptions record their associated traceback. On Python 3, raise and raise ex will both preserve the original traceback, but raise ex will add an extra record to the traceback for the raise ex line.
Also, Python uses a strange traceback construction system where an exception has no stack trace when initially raised, and each time it bubbles up through a stack frame, an additional frame record is added to the stack trace. (A record is also added for a raise with 1 or 2 arguments, but not 0 or 3, hence the difference in the previous paragraph.) Due to this traceback construction system, a traceback won't contain any information about frames the exception didn't propagate to, and unusual exception handling patterns can cause a traceback to look nothing like what the stack looked like at the point of the initial raise.
My question seems rather simply, but I didn't find any post to this particular issue. I need my own custom exception class deriving from ValueError to print the expected type (standard error msg) as well as the type that was entered (with custom text).
class MyOwnException(ValueError):
...
try:
raise MyOwnException ( int('str') ) #not sure what to do here, as I only want to
#raise the exception if incorrect value type
except MyOwnException as e:
print "Error: Expected type", e.expType() #int
print "Error: Entered type", e.entType() #string
To add to the above and raising my custom exception via the built-in ValueError:
class MyOwnException(ValueError):
def __init__(self, value):
self.value = value
print "Error: Expected type", type(self.value) #int
print "Error type", self.value #how to return expected value type?
try:
int('str')
except ValueError as e:
raise MyOwnException(e)
I would very much appreatiate any help in this regard. Thanks very much!
Cheers, Manuel
It's generally the case that when raising custom exceptions, you have to catch a more generic exception and re-raise a different one. So for example,
>>> class MoofiError(ValueError):
... pass
...
>>> try:
... int('a')
... except ValueError:
... raise MoofiError, 'you did something wrong, fool'
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
__main__.MoofiError: you did something wrong, fool
The int function will always return a ValueError, rather than your custom type.
In order to throw a different exception, you would have to wrap int to catch ValueError and then raise an exception of your choice (which could include the failing value, as you want).
This code creates a new exception class
class MyOwnException(ValueError): pass
However nothing will make someone else's code raise your exception - you can only raise it in yours.
This is a followup to my question Hang in Python script using SQLAlchemy and multiprocessing. As discussed in that question, pickling exceptions is problematic in Python. This is usually not a issue, but one case when it is, is when errors occur in the python multiprocessing module. Since multiprocessing moves objects around by pickling, if an error occurs inside a multiprocessing process, the entire process may hang, as demonstrated in that question.
One possible approach is to fix all the problematic exceptions, as discussed in that question. This is not easy, since one cannot easily know in advance which exceptions may be called. An alternative approach, which was suggested by lbolla in an answer to the question, is to catch the exception, construct an equivalent harmless exception, and then rethrow.
However, I'm not sure of exactly how to do this. Consider the following code.
class BadExc(Exception):
def __init__(self, message, a):
'''Non-optional param in the constructor.'''
Exception.__init__(self, message)
self.a = a
import sys
try:
try:
#print foo
raise BadExc("bad exception error message", "a")
except Exception, e:
raise Exception(e.__class__.__name__ + ": " +str(e)), None, sys.exc_info()[2]
except Exception, f:
pass
import cPickle
a = cPickle.dumps(f)
l = cPickle.loads(a)
print "raising error"
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
This code pickles and unpickles the exception, and then throws it, giving the error
raising error
Traceback (most recent call last):
File "<stdin>", line 11, in <module>
Exception: BadExc: bad exception error message
Credits to Glenn Maynard's answer to "“Inner exception” (with traceback) in Python?".
This has the important stuff, namely the traceback, the error message, and the exception type, so this might be the best one can do. But ideally I'd like something that looks exactly like the original exception, namely
Traceback (most recent call last):
File "<stdin>", line 11, in <module>
__main__.BadExc: bad exception error message
or more generally, with the name of the exception in the front, rather than Exception. Is this possible?
Alternatively, instead of the BadExc class, one can use the print foo statement instead, which gives a NameError. However, this exception does not require special handling.
You can override sys.excepthook to achieve what you want. It at least works for this example, but it's pretty hacky so please test and no promises :-)
import sys
def excepthook_wrapper(type, value, traceback):
if len(value.args) == 2:
name, msg = value.args
value.args = (msg,)
sys.__excepthook__(name, value, traceback)
else:
sys.__excepthook__(type, value, traceback)
sys.excepthook = excepthook_wrapper
(Edit: I'm not really happy with this because now 'normal' Exceptions with two arguments will get handled differently too. Possible solution, 'tag' your special Exceptions by passing "PICKLED" as a first argument and then check for that, instead of checking for the length of the args.)
And then create the Exception with two arguments, the name (__module__.__class__) and the Exception message (str(e)):
try:
try:
#print foo
raise BadExc("bad exception error message", "a")
except Exception, e:
cls = e.__class__
if hasattr(cls, '__module__'):
name = '{0}.{1}'.format(cls.__module__, cls.__name__)
else:
name = cls.__name__
raise Exception(name, str(e)), None, sys.exc_info()[2]
except Exception, f:
pass
Then this:
import cPickle
a = cPickle.dumps(f)
l = cPickle.loads(a)
print "raising error"
raise sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]
Prints:
raising error
Traceback (most recent call last):
File "test.py", line 18, in <module>
raise BadExc("bad exception error message", "a")
__main__.BadExc: bad exception error message
My background is in C# and I've just recently started programming in Python. When an exception is thrown I typically want to wrap it in another exception that adds more information, while still showing the full stack trace. It's quite easy in C#, but how do I do it in Python?
Eg. in C# I would do something like this:
try
{
ProcessFile(filePath);
}
catch (Exception ex)
{
throw new ApplicationException("Failed to process file " + filePath, ex);
}
In Python I can do something similar:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file ' + filePath, e)
...but this loses the traceback of the inner exception!
Edit: I'd like to see both exception messages and both stack traces and correlate the two. That is, I want to see in the output that exception X occurred here and then exception Y there - same as I would in C#. Is this possible in Python 2.6? Looks like the best I can do so far (based on Glenn Maynard's answer) is:
try:
ProcessFile(filePath)
except Exception as e:
raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]
This includes both the messages and both the tracebacks, but it doesn't show which exception occurred where in the traceback.
Python 3
In python 3 you can do the following:
try:
raise MyExceptionToBeWrapped("I have twisted my ankle")
except MyExceptionToBeWrapped as e:
raise MyWrapperException("I'm not in a good shape") from e
This will produce something like this:
Traceback (most recent call last):
...
MyExceptionToBeWrapped: ("I have twisted my ankle")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
...
MyWrapperException: ("I'm not in a good shape")
Python 2
It's simple; pass the traceback as the third argument to raise.
import sys
class MyException(Exception): pass
try:
raise TypeError("test")
except TypeError, e:
raise MyException(), None, sys.exc_info()[2]
Always do this when catching one exception and re-raising another.
Python 3 has the raise ... from clause to chain exceptions. Glenn's answer is great for Python 2.7, but it only uses the original exception's traceback and throws away the error message and other details. Here are some examples in Python 2.7 that add context information from the current scope into the original exception's error message, but keep other details intact.
Known Exception Type
try:
sock_common = xmlrpclib.ServerProxy(rpc_url+'/common')
self.user_id = sock_common.login(self.dbname, username, self.pwd)
except IOError:
_, ex, traceback = sys.exc_info()
message = "Connecting to '%s': %s." % (config['connection'],
ex.strerror)
raise IOError, (ex.errno, message), traceback
That flavour of raise statement takes the exception type as the first expression, the exception class constructor arguments in a tuple as the second expression, and the traceback as the third expression. If you're running earlier than Python 2.2, see the warnings on sys.exc_info().
Any Exception Type
Here's another example that's more general purpose if you don't know what kind of exceptions your code might have to catch. The downside is that it loses the exception type and just raises a RuntimeError. You have to import the traceback module.
except Exception:
extype, ex, tb = sys.exc_info()
formatted = traceback.format_exception_only(extype, ex)[-1]
message = "Importing row %d, %s" % (rownum, formatted)
raise RuntimeError, message, tb
Modify the Message
Here's another option if the exception type will let you add context to it. You can modify the exception's message and then reraise it.
import subprocess
try:
final_args = ['lsx', '/home']
s = subprocess.check_output(final_args)
except OSError as ex:
ex.strerror += ' for command {}'.format(final_args)
raise
That generates the following stack trace:
Traceback (most recent call last):
File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module>
s = subprocess.check_output(final_args)
File "/usr/lib/python2.7/subprocess.py", line 566, in check_output
process = Popen(stdout=PIPE, *popenargs, **kwargs)
File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
errread, errwrite)
File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
raise child_exception
OSError: [Errno 2] No such file or directory for command ['lsx', '/home']
You can see that it shows the line where check_output() was called, but the exception message now includes the command line.
In Python 3.x:
raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)
or simply
except Exception:
raise MyException()
which will propagate MyException but print both exceptions if it will not be handled.
In Python 2.x:
raise Exception, 'Failed to process file ' + filePath, e
You can prevent printing both exceptions by killing the __context__ attribute. Here I write a context manager using that to catch and change your exception on the fly:
(see http://docs.python.org/3.1/library/stdtypes.html for expanation of how they work)
try: # Wrap the whole program into the block that will kill __context__.
class Catcher(Exception):
'''This context manager reraises an exception under a different name.'''
def __init__(self, name):
super().__init__('Failed to process code in {!r}'.format(name))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None:
self.__traceback__ = exc_tb
raise self
...
with Catcher('class definition'):
class a:
def spam(self):
# not really pass, but you get the idea
pass
lut = [1,
3,
17,
[12,34],
5,
_spam]
assert a().lut[-1] == a.spam
...
except Catcher as e:
e.__context__ = None
raise
I don't think you can do this in Python 2.x, but something similar to this functionality is part of Python 3. From PEP 3134:
In today's Python implementation, exceptions are composed of three
parts: the type, the value, and the traceback. The 'sys' module,
exposes the current exception in three parallel variables, exc_type,
exc_value, and exc_traceback, the sys.exc_info() function returns a
tuple of these three parts, and the 'raise' statement has a
three-argument form accepting these three parts. Manipulating
exceptions often requires passing these three things in parallel,
which can be tedious and error-prone. Additionally, the 'except'
statement can only provide access to the value, not the traceback.
Adding the 'traceback' attribute to exception values makes all
the exception information accessible from a single place.
Comparison to C#:
Exceptions in C# contain a read-only 'InnerException' property that
may point to another exception. Its documentation [10] says that
"When an exception X is thrown as a direct result of a previous
exception Y, the InnerException property of X should contain a
reference to Y." This property is not set by the VM automatically;
rather, all exception constructors take an optional 'innerException'
argument to set it explicitly. The 'cause' attribute fulfills
the same purpose as InnerException, but this PEP proposes a new form
of 'raise' rather than extending the constructors of all exceptions.
C# also provides a GetBaseException method that jumps directly to
the end of the InnerException chain; this PEP proposes no analog.
Note also that Java, Ruby and Perl 5 don't support this type of thing either. Quoting again:
As for other languages, Java and Ruby both discard the original
exception when another exception occurs in a 'catch'/'rescue' or
'finally'/'ensure' clause. Perl 5 lacks built-in structured
exception handling. For Perl 6, RFC number 88 [9] proposes an exception
mechanism that implicitly retains chained exceptions in an array
named ##.
For maximum compatibility between Python 2 and 3, you can use raise_from in the six library. https://six.readthedocs.io/#six.raise_from . Here is your example (slightly modified for clarity):
import six
try:
ProcessFile(filePath)
except Exception as e:
six.raise_from(IOError('Failed to process file ' + repr(filePath)), e)
You could use my CausedException class to chain exceptions in Python 2.x (and even in Python 3 it can be useful in case you want to give more than one caught exception as cause to a newly raised exception). Maybe it can help you.
Maybe you could grab the relevant information and pass it up? I'm thinking something like:
import traceback
import sys
import StringIO
class ApplicationError:
def __init__(self, value, e):
s = StringIO.StringIO()
traceback.print_exc(file=s)
self.value = (value, s.getvalue())
def __str__(self):
return repr(self.value)
try:
try:
a = 1/0
except Exception, e:
raise ApplicationError("Failed to process file", e)
except Exception, e:
print e
Assuming:
you need a solution, which works for Python 2 (for pure Python 3 see raise ... from solution)
just want to enrich the error message, e.g. providing some additional context
need the full stack trace
you can use a simple solution from the docs https://docs.python.org/3/tutorial/errors.html#raising-exceptions:
try:
raise NameError('HiThere')
except NameError:
print 'An exception flew by!' # print or log, provide details about context
raise # reraise the original exception, keeping full stack trace
The output:
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere
It looks like the key piece is the simplified 'raise' keyword that stands alone. That will re-raise the Exception in the except block.