New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Traceback from __exit__ method is misleading #69724
Comments
This is at least the second time that I've wasted a bunch of debugging time being mislead by the traceback from __exit__. Perhaps I will remember from now on, but...I wonder if it would be possible/practical to improve the situation. The problem is that in the traceback printed when __exit__ throws an error, the source line printed before the source line from inside the __exit__ is the last line of the 'with' block. I kept staring at that line trying to figure out how the exception could possibly be getting raised from that line...which of course it wasn't really, it was the "line" in between that one and the next one, a psuedo-line representing the exit from the block. I don't know if it would be worth the effort that would be required to somehow clarify this in the traceback (I'm not even sure what that would *look* like...maybe the first line of the with block instead?), but I thought I'd raise the issue to see if anyone had any clever ideas. (Hopefully I haven't done this before...I tried searching for similar issues and didn't find anything.) |
I guess you are mainly complaining about when __exit__() implementation is built into the interpreter. E.g., given demo.py: with open("/dev/full", "wb") as file:
file.write(b"data")
print("All is well")
print("Not executed") the output looks like All is well
Traceback (most recent call last):
File "demo.py", line 3, in <module>
print("All is well")
OSError: [Errno 28] No space left on device When __exit__() is implemented natively in Python, you get more of a hint: Traceback (most recent call last):
File "demo.py", line 4, in <module>
print("All is well")
File "/home/proj/python/cpython/Lib/_pyio.py", line 457, in __exit__
self.close()
File "/home/proj/python/cpython/Lib/_pyio.py", line 773, in close
self.flush()
File "/home/proj/python/cpython/Lib/_pyio.py", line 1210, in flush
self._flush_unlocked()
File "/home/proj/python/cpython/Lib/_pyio.py", line 1217, in _flush_unlocked
n = self.raw.write(self._write_buf)
File "/home/proj/python/cpython/Lib/_pyio.py", line 1614, in write
return os.write(self._fd, b)
OSError: [Errno 28] No space left on device Maybe another option would be to insert a virtual frame in the built-in version of the traceback: Traceback (most recent call last):
File "demo.py", line 3, in <module>
print("All is well")
File "<built in>", line ???, in <context manager exit>
OSError: [Errno 28] No space left on device |
What caught me out was more like this (but more complicated so it wasn't obvious the line mentioned wasn't part of the exception chain; in fact it looked like it was since it was a line that could very well have raised the exception in question):
def foo():
with cm():
print('all is well')
class cm:
def __enter__(self):
return self
def __exit__(*args, **kw):
raise Exception('exception in __exit__')
do_something()
rdmurray@pydev:~/python/p36>./python temp.py
all is well
caught in do_something
Traceback (most recent call last):
File "temp.py", line 20, in <module>
do_something()
File "temp.py", line 3, in do_something
foo()
File "temp.py", line 10, in foo
print('all is well')
File "temp.py", line 18, in __exit__
raise Exception('exception in __exit__')
Exception: exception in __exit__ The confusing line in the traceback is "print('all is well')", which obviously isn't raising the exception. |
In Martin's example, I would expect the first line of the traceback to be the file write that raised the exception, not the print that is never executed. If print were writing to a different device than the first line, the traceback would be positively misleading. In David's example, the print executed just fine, so it should not appear in the traceback either. The replacement, if any, might be the with statement itself. The 'pseudo-line' that triggers the __exit__ call is really the dedent on the next line, but printing the whole next line would be wrong too. |
Explanation of my example because it is tricky: The print is actually executed. Then the context manager exits, and the built-in buffered file object tries to flush its buffer and fails. The earlier write() call succeeds, because it only writes to the Python buffer, not the OS file descriptor. |
Yes, I think the line that should be in the traceback in my example is the 'with' line. I have no clue how hard that would be to achieve. |
See also bpo-29922. |
This is doable, the feature that decides which line is shown in the traceback is the I have attached a python script which actually achieves this. This is the error thrown by the test script, it has 2 cascading failures in exit so that I could confirm it worked for nested withs and it absolutely points to the relevant with statement as I have highlighted on the traceback, Traceback (most recent call last):
File "/Users/tadhgmcdonald-jensen/Documents/test.py", line 71, in my_test
>> with Test(True) as will_fail_first: <<<--------- HERE
File "/Users/tadhgmcdonald-jensen/Documents/test.py", line 67, in __exit__
raise Exception("error in exit")
Exception: error in exit
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/tadhgmcdonald-jensen/Documents/test.py", line 78, in <module>
my_test()
File "/Users/tadhgmcdonald-jensen/Documents/test.py", line 70, in my_test
>> with Test(True) as fail_during_handling: <<<--------- HERE
File "/Users/tadhgmcdonald-jensen/Documents/test.py", line 67, in __exit__
raise Exception("error in exit")
Exception: error in exit |
uploaded |
This is fix since 3.10, I'm guessing by @markshannon 's work on line numbers?
And in 3.10 as well:
|
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: