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:
- Where did my first exception go?
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).
- Why is only the outermost exception catchable?
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',))
- How do I peel off the outermost exception and reraise the earlier exceptions?
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.