I have nested try except blocks that appear to print the wrong exception.
This is on python 2.7.16
import logging
try:
raise ValueError('1')
except Exception as e:
try:
raise KeyError('2')
except KeyError:
logging.exception(e)
The output is
ERROR:root:1
Traceback (most recent call last):
File "<stdin>", line 5, in <module>
KeyError: '2'
But I would expect the output to be for the first exception as that is what is assigned to e.
Follow up question
If I wanted to save the execution info from the first exception, how would I do that?
Oh, self-answer here: It's only the execution info that is for the second exception. The actual message is from the first one.
Related
Let's assume you have some simple code that you don't control (eg: it's in a module you're using):
def example():
try:
raise TypeError("type")
except TypeError:
raise Exception("device busy")
How would I go about accessing the TypeError in this traceback in order to handle it?
Traceback (most recent call last):
File "/usr/local/google/home/dthor/dev/pyle/pyle/fab/visa_instrument/exception_helpers.py", line 3, in example
raise TypeError("type")
TypeError: type
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/google/home/dthor/dev/pyle/pyle/fab/visa_instrument/exception_helpers.py", line 7, in <module>
example()
File "/usr/local/google/home/dthor/dev/pyle/pyle/fab/visa_instrument/exception_helpers.py", line 5, in example
raise Exception("device busy")
Exception: device busy
I can do the below, but i'm not happy with it because I'm doing string comparison - meaning things would break if the underlying module changes what string they raise (I don't control example()):
try:
example()
except Exception as err:
if "device busy" in str(err):
# do the thing
pass
# But raise any other exceptions
raise err
I'd much rather have:
try:
example()
except Exception as err:
if TypeError in err.other_errors: # magic that doesn't exist
# do the thing
pass
raise err
or even
try:
example()
except TypeError in exception_stack: # Magic that doesn't exist
# do the thing
pass
except Exception:
I'm investigating the traceback module and sys.exc_info(), but don't have anything concrete yet.
Followup: would things be different if the exception was chained? Eg: raise Exception from the_TypeError_exception
Check the __context__ of the exception:
>>> try:
... example()
... except Exception as e:
... print(f"exception: {e!r}")
... print(f"context: {e.__context__!r}")
...
exception: Exception('device busy')
context: TypeError('type')
If you use a chained exception, the original exception will also be accessible via the __cause__ attribute.
https://docs.python.org/3/tutorial/errors.html#exception-chaining
>>>
>>> def func():
... raise IOError
...
>>> try:
... func()
... except IOError as exc:
... raise RuntimeError('Failed to open database') from exc
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in func
OSError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
RuntimeError: Failed to open database
Exception chaining happens automatically when an exception is raised inside an except or finally section. So what is the purpose of using the from exc in raise RuntimeError('Failed to open database') from exc? Isn't it implicit?
You can always check the PEP which introduces a feature. In this case, you may read this section of PEP 3141.
The use of the syntax raise EXCEPTION from CAUSE allows for an explicit chaining of exceptions. Like in the example given on that page, if you want to raise only a certain error in an API, but also want to highlight the original exception raising the issue, you can use this way of chaining exceptions.
So it just offers a more explicit way of doing that, while the "normal" syntax raise EXCEPTION only implicitly chains the exceptions.
Is this understandable? Let me know if you don't understand it.
Consider the simple example:
def f():
try:
raise TypeError
except TypeError:
raise ValueError
f()
I want to catch TypeError object when ValueError is thrown after f() execution. Is it possible to do it?
If I execute function f() then python3 print to stderr all raised exceptions of exception chain (PEP-3134) like
Traceback (most recent call last):
File "...", line 6, in f
raise TypeError
TypeError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "...", line 11, in <module>
f()
File "...", line 8, in f
raise ValueError
ValueError
So I would get the list of all exceptions of exception chain or check if exception of some type (TypeError in the above example) exists in exception chain.
Python 3 has a beautiful syntactic enhancement on exceptions handling. Instead of plainly raising ValueError, you should raise it from a caught exception, i.e.:
try:
raise TypeError('Something awful has happened')
except TypeError as e:
raise ValueError('There was a bad value') from e
Notice the difference between the tracebacks. This one uses raise from version:
Traceback (most recent call last):
File "/home/user/tmp.py", line 2, in <module>
raise TypeError('Something awful has happened')
TypeError: Something awful has happened
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/user/tmp.py", line 4, in <module>
raise ValueError('There was a bad value') from e
ValueError: There was a bad value
Though the result may seem similar, in fact it is rather different! raise from saves the context of the original exception and allows one to trace all the exceptions chain back - which is impossible with simple raise.
To get the original exception, you simply have to refer to new exception's __context__ attribute, i.e.
try:
try:
raise TypeError('Something awful has happened')
except TypeError as e:
raise ValueError('There was a bad value') from e
except ValueError as e:
print(e.__context__)
>>> Something awful has happened
Hopefully that is the solution you were looking for.
For more details, see PEP 3134 -- Exception Chaining and Embedded Tracebacks
I'm learning to use python. I just came across this article:
http://nedbatchelder.com/blog/200711/rethrowing_exceptions_in_python.html
It describes rethrowing exceptions in python, like this:
try:
do_something_dangerous()
except:
do_something_to_apologize()
raise
Since you re-throw the exception, there should be an "outer catch-except" statement. But now, I was thinking, what if the do_something_to_apologize() inside the except throws an error. Which one will be caught in the outer "catch-except"? The one you rethrow or the one thrown by do_something_to_apologize() ?
Or will the exception with the highest priotiry be caught first?
Try it and see:
def failure():
raise ValueError, "Real error"
def apologize():
raise TypeError, "Apology error"
try:
failure()
except ValueError:
apologize()
raise
The result:
Traceback (most recent call last):
File "<pyshell#14>", line 10, in <module>
apologize()
File "<pyshell#14>", line 5, in apologize
raise TypeError, "Apology error"
TypeError: Apology error
The reason: the "real" error from the original function was already caught by the except. apologize raises a new error before the raise is reached. Therefore, the raise in the except clause is never executed, and only the apology's error propagates upward. If apologize raises an error, Python has no way of knowing that you were going to raise a different exception after apologize.
Note that in Python 3, the traceback will mention both exceptions, with a message explaining how the second one arose:
Traceback (most recent call last):
File "./prog.py", line 9, in <module>
File "./prog.py", line 2, in failure
ValueError: Real error
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "./prog.py", line 11, in <module>
File "./prog.py", line 5, in apologize
TypeError: Apology error
However, the second exception (the "apology" exception) is still the only one that propagates outward and can be caught by a higher-level except clause. The original exception is mentioned in the traceback but is subsumed in the later one and can no longer be caught.
The exception thrown by do_something_to_apologize() will be caught. The line containing raise will never run, because of the exception thrown by do_something_to_apologize. Also, I don't believe there is any idea of "priority" in python exceptions.
I believe a better idea is to use
raise NewException("Explain why") from CatchedException
pattern. In particular, considering Python 3 and the example given by #BrenBarn I use following
def failure():
raise ValueError("Real error")
try:
failure()
except ValueError as ex:
raise TypeError("Apology error") from ex
which yields
--------- ValueError----
Traceback (most recent call last)
4 try:
----> 5 failure()
6 except ValueError as ex:
1 def failure():
----> 2 raise ValueError("Real error")
3
ValueError: Real error
The above exception was the direct cause of the following exception:
-----TypeError-----
Traceback (most recent call last)
5 failure()
6 except ValueError as ex:
----> 7 raise TypeError("Apology error") from ex
TypeError: Apology error
Consider this try/except block I use for checking error message stored in e.
Try/Catch to get the e
queryString = "SELECT * FROM benchmark WHERE NOC = 2"
try:
res = db.query(queryString)
except SQLiteError, e:
# `e` has the error info
print `e`
The e object here contains nothing more than the above string. When python reports an unhandled error, however, it shows a pretty detailed info as below:
Traceback (most recent call last):
File "fool.py", line 1, in
open("abc.zyz", "r")
IOError: [Errno 2] No such file or directory: 'abc.zyz'
My question is, how can I get the information such as above (the file and the line number etc.)? Or, if e contains this info, how is it stored inside it?
This will show the trace to the error.
import traceback
try:
res = db.query(queryString)
except SQLiteError, e:
# `e` has the error info
print `e`
for tb in traceback.format_tb(sys.exc_info()[2]):
print tb
Like the first 2 answers, use traceback. Here is a more complete example.
import traceback
def foo():
raise RuntimeError('we have a problem')
try:
foo()
except:
traceback.print_exc()
When you run it, you'll see
Traceback (most recent call last):
File "C:\0\tmp\x.py", line 6, in <module>
foo()
File "C:\0\tmp\x.py", line 3, in foo
raise RuntimeError('we have a problem')
RuntimeError: we have a problem
See the traceback library.
If you want to just pass errors up the chain instead of modifying them, you can just use raise within an except block, which will then act like the except block isn't there (aside from any conditional logic/side effects you may have done before the raise).