Raising exceptions when an exception is already present in Python 3

40,336

Solution 1

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

Solution 2

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

Solution 3

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.

Solution 4

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:

  1. 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).

  1. 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',))
  1. 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.

Solution 5

  1. It got thrown out.
  2. Only one exception can be "active" at a time per thread.
  3. You can't, unless you encapsulate the earlier exception in the later exception somehow.
Share:
40,336
Matt Joiner
Author by

Matt Joiner

About Me I like parsimonious code, with simple interfaces and excellent documentation. I'm not interested in enterprise, boiler-plate, or cookie-cutter nonsense. I oppose cruft and obfuscation. My favourite languages are Go, Python and C. I wish I was better at Haskell. Google+ GitHub Bitbucket Google code My favourite posts http://stackoverflow.com/questions/3609469/what-are-the-thread-limitations-when-working-on-linux-compared-to-processes-for/3705919#3705919 http://stackoverflow.com/questions/4352425/what-should-i-learn-first-before-heading-to-c/4352469#4352469 http://stackoverflow.com/questions/6167809/how-much-bad-can-be-done-using-register-variables-in-c/6168852#6168852 http://stackoverflow.com/questions/4141307/c-and-c-source-code-profiling-tools/4141345#4141345 http://stackoverflow.com/questions/3463207/how-big-can-a-malloc-be-in-c/3486163#3486163 http://stackoverflow.com/questions/4095637/memory-use-of-stl-data-structures-windows-vs-linux/4183178#4183178

Updated on July 09, 2022

Comments

  • Matt Joiner
    Matt Joiner almost 2 years

    What happens to my first exception (A) when the second (B) is raised in the following code?

    class A(Exception): pass
    class B(Exception): pass
    
    try:
        try:
            raise A('first')
        finally:
            raise B('second')
    except X as c:
        print(c)
    

    If run with X = A I get:

    Traceback (most recent call last):
      File "raising_more_exceptions.py", line 6, in 
        raise A('first')
    __main__.A: first
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "raising_more_exceptions.py", line 8, in 
        raise B('second')
    __main__.B: second

    But if X = B I get:

    second

    Questions

    1. Where did my first exception go?
    2. Why is only the outermost exception catchable?
    3. How do I peel off the outermost exception and reraise the earlier exceptions?

    Update0

    This question specifically addresses Python 3, as its exception handling is quite different to Python 2.

  • Matt Joiner
    Matt Joiner almost 13 years
    If it got thrown out, why am I getting a full traceback when the exception is not caught?
  • Ignacio Vazquez-Abrams
    Ignacio Vazquez-Abrams almost 13 years
    Because the tool that you're using to run the script has installed a global exception handler, and it has noticed the double exception.
  • Ignacio Vazquez-Abrams
    Ignacio Vazquez-Abrams almost 13 years
    I see now, Python 3.x has different behavior. It is the interpreter itself catching it. In 2.x the first exception is silently dropped.
  • Lennart Regebro
    Lennart Regebro almost 13 years
    @Matt: It doesn't go anywhere. I do realize I had a brainfart and updated my answer, though.
  • Admin
    Admin almost 10 years
    How does one handle this situation in doctests?
  • gerrit
    gerrit about 7 years
    Or can use raise x from y?
  • Lennart Regebro
    Lennart Regebro about 7 years
    If you only support Python 3, yes. Which is reasonable now, but not in 2011.
  • James McCorrie
    James McCorrie about 6 years
    Thanks, this solved my issue... wanted to raise a custom exception from a KeyError in a base class method. My List is a sub class attribute and sub class objects are dynamically generated. The default KeyError trackback didn't say which sub class the error occurred in, so I wanted to suppress it and raise a new exception with more info. "Raise X from None" allows me to do this.
  • Alexey Shrub
    Alexey Shrub over 5 years
    If you want to save original traceback you can use: raise OtherException(...).with_traceback(tb) as described in docs docs.python.org/3/library/exceptions.html#BaseException
  • user2357112
    user2357112 over 4 years
    This isn't what question 3 asked. Question 3 asked how to remove the B and reraise the A, not how to raise just the B and suppress the A.