What happens to my first exception (A
) when the second (B
) is raised in the following code?
class A(Exception): pass
class B(Except
Answering to question 3, you can use:
raise B('second') from None
Which will remove the exception A
traceback.
Traceback (most recent call last):
File "raising_more_exceptions.py", line 8, in
raise B('second')
__main__.B: second
Pythons exception handling will only deal with one exception at a time. However, exception objects are subject to the same variable rules and garbage collection as everything else. Hence, if you save the exception object in a variable somewhere you can deal with it later, even if another exception is raised.
In your case, when an exception is raised during the "finally" statement, Python 3 will print out the traceback of the first exception before the one of the second exception, to be more helpful.
A more common case is that you want to raise an exception during an explicit exception handling. Then you can "save" the exception in the next exception. Just pass it in as a parameter:
>>> class A(Exception):
... pass
...
>>> class B(Exception):
... pass
...
>>> try:
... try:
... raise A('first')
... except A as e:
... raise B('second', e)
... except Exception as c:
... print(c.args[1])
...
first
As you see you can now access the original exception.
I believe all the ingredients to answer your question(s) are already in the existing answers. Let me combine and elaborate.
Let me repeat your question's code to provide line number references:
1 class A(Exception): pass
2 class B(Exception): pass
3
4 try:
5 try:
6 raise A('first')
7 finally:
8 raise B('second')
9 except X as c:
10 print(c)
So to answer your questions:
Your first exception A
is raised in line 6. The finally
clause in line 7 is always executed as soon as the try
block (lines 5-6) is left, regardless if it is left because of successful completion or because of a raised exception.
While the finally
clause is being executed, line 8 raises another exception B
. As Lennart and Ignazio have pointed out, only one exception, the one that is most recently being raised, can be kept track of. So as soon as B
is raised, the overall try
block (lines 4-8) is quit and the exception B
is being caught by the except
statement in line 9 if it matches (if X
is B
).
Hopefully this is clear now from my explanation of 1. You could catch the inner/lower/first exception, though. To merge in Lennart's answer, slightly modified, here's how to catch both:
class A(Exception): pass
class B(Exception): pass
try:
try:
raise A('first')
except A as e:
raise B('second', e)
except Exception as c:
print(c)
The output is:
('second', A('first',))
In Lennart's example the solution to this question is the line except A as e
where the inner/lower/first exception is being caught and stored in variable e
.
As a general gut-feeling of when to catch exceptions, when to ignore them, and when to re-raise, maybe this question and Alex Martelli's answer help.
The 'causing' exception is available as c.__context__ in your last exception handler. Python is using this information to render a more useful traceback. Under Python 2.x the original exception would have been lost, this is for Python 3 only.
Typically you would use this to throw a consistent exception while still keeping the original exception accessible (although it's pretty cool that it happens automatically from an exception handler, I didn't know that!):
try:
do_something_involving_http()
except (URLError, socket.timeout) as ex:
raise MyError('Network error') from ex
More info (and some other pretty useful things you can do) here: http://docs.python.org/3.3/library/exceptions.html