Nested causes in nested exceptions in python [duplicate] - python

This question already has answers here:
"Inner exception" (with traceback) in Python?
(9 answers)
Closed 8 years ago.
Is there a way to provide information about the cause of an inner exception when passing it up the chain (like it's possible in java with the cause property of Exception class).
Please consider the following "python pseudo code" (without 100 % correct and invented function and class names)
try:
clientlib.receive_data_chunk()
except ClientException as clientException:
raise RuntimeError("reading from client failed"
+ " (see nested exceptions for details)", cause=clientException)
and in clientlib.py
def receive_data_chunk():
try:
chunk = socket.read(20)
return chunk
except IOException as iOException:
raise ClientException("couldn't read from client", cause = iOException)
If not within native python what would be best practice to achieve what I want to do?
Please note that I want to preserve both stacktraces of the inner and the outer exception, i.e. the following solution is not satisfying:
import sys
def function():
try:
raise ValueError("inner cause")
except Exception:
_, ex, traceback = sys.exc_info()
message = "outer explanation (see nested exception for details)"
raise RuntimeError, message, traceback
if __name__ == "__main__":
function()
produces only the following output:
Traceback (most recent call last):
File "a.py", line 13, in <module>
function()
File "a.py", line 6, in function
raise ValueError("inner cause")
RuntimeError: outer explanation (see nested exception for details)
I cannot see where the RuntimeError occured, so in my understanding the outer stacktrace is lost.

In Python 3, you can use the from keyword to specify an inner exception:
raise ClientException(...) from ioException
You get a traceback that looks like this:
Traceback (most recent call last):
...
IOException: timeout
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
...
ClientException: couldn't read from client

Related

Why 'raise EXCEPTION from CAUSE', not 'raise (EXCEPTION from CAUSE)'? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
Let me elaborate on the mildly cryptic question title.
In Python 3 we've got neat exception chaining feature, that lets you add more context to exceptions in the process of their propagation.
Recently, I wanted to attach extra exception information to the exception, but without throwing it straightaway. The code was doing some defensive processing in generator, so I wanted to yield it instead, so I tried something like:
def gen():
for row in csv_file:
try:
yield parse(row)
except Exception as e:
yield RuntimeError(f"Bad row: {row}") from e
To my disappointment, that didn't work! Turned out that raise EXCEPTION from CAUSE is a compound operator as PEP-3134 explains.
To get around this in my code, I set __cause__ = e manually and carried on. However, it still bothered me why it was implemented that way.
Imagine instead we had an operator from with the following syntax: EXCEPTION from CAUSE, doing exactly what raise .. from .. is doing, except throwing, and returning new Exception instead. That way:
it's compatible, you can still use raise .. from .. syntax (now internally it'd be parsed as raise (.. from ..))
it would make everything more composable, allowing to attach cause without necessarily throwing (in particular my usecase). I can that it's a bit unusual use of exceptions though.
presumably, that would also make implementation simpler, since you'd not need to modify and clash exiting primitive (simple raise operator). I see it as a main argument for my 'alternative form' of the operator from the perspective of Python language developer.
I've gone through PEP/googled but hasn't really found any rationale. I experimented to check if there is something else apart from __cause__ being set, but it doesn't seem so. I'm attaching bits of code that I used to check that:
def failing():
raise RuntimeError("function failed!")
def cause_from():
try:
failing()
except Exception as e:
raise RuntimeError("extra info") from e
def cause_manual():
try:
failing()
except Exception as e:
ee = RuntimeError("extra info")
ee.__cause__ = e
raise ee
It results in the same traceback:
$ python3 -c 'import exc; exc.cause_from()'
Traceback (most recent call last):
File "/tmp/exc.py", line 7, in cause_from
failing()
File "/tmp/exc.py", line 2, in failing
raise RuntimeError("function failed!")
RuntimeError: function failed!
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/tmp/exc.py", line 9, in cause_from
raise RuntimeError("extra info") from e
RuntimeError: extra info
$ python3 -c 'import exc; exc.cause_manual()'
Traceback (most recent call last):
File "/tmp/exc.py", line 13, in cause_manual
failing()
File "/tmp/exc.py", line 2, in failing
raise RuntimeError("function failed!")
RuntimeError: function failed!
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/tmp/exc.py", line 17, in cause_manual
raise ee
RuntimeError: extra info
The only difference in traceback is the line where exception was thrown (raise RuntimeError("extra info") from e vs raise ee). The cause_from version looks somewhat more readable.
So it's the only reason I see for that choice of syntax structure, I wonder if it's also the intended motivation? Is it possible that I miss some subtle effect of raise .. from .. as a compound operator?

Nested try-except logging wrong exception

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.

Print exception details recursively while using try except [duplicate]

This question already has answers here:
How to catch and print the full exception traceback without halting/exiting the program?
(18 answers)
Closed 5 years ago.
#!/usr/bin/env python
#import re
def test_ex1(input):
if re.match(r'^[A-Z]+$',input):
print 'Match'
return True
print 'No Match'
return False
#test_ex1('ABC')
try:
test_ex1('ABC')
except Exception:
raise Exception
If I run the above program,it will print following exception message.
a:~/Python> python test.py
Traceback (most recent call last):
File "test.py", line 18, in <module>
raise Exception
Exception
What is the right way in Python to make it print the following stack trace while catching exception using try except without changing test_ex1 subroutine?
Traceback (most recent call last):
File "test.py", line 15, in <module>
test_ex1('ABC')
File "test.py", line 8, in test_ex1
if re.match(r'^[A-Z]+$',input):
NameError: global name 're' is not defined
One way is using the traceback module
import traceback
try:
re.match()
except Exception:
traceback.print_exc()
I once used following to print all traceback as:
import sys
try:
# code here
except Exception:
print(sys.exc_info()[0])
The raise keyword is being used to throw your error up to the stack and handle at upper level of function call.

rethrowing python exception. Which to catch?

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

How can I get better error information with try/except in Python

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

Categories