-
-
Notifications
You must be signed in to change notification settings - Fork 30.5k
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
Bug in linecache when raising exceptiongroup that contains ZeroDivisionError
+ other exception.
#101517
Comments
Can you produce a repro without asyncio? I suspect you can get this by just raising and catching two errors and then raise an ExceptionGroup. @iritkatriel |
It's something to do with a breakpoint as the last thing in an except block. Without asyncio:
|
Something to do with frame initialisation? @markshannon @brandtbucher
|
I don't see the problem going away when I add the |
This does reproduce the issue for me, though: try:
raise ExceptionGroup("eg", [KeyError(), ValueError()])
except* KeyError:
breakpoint()
# pass # <-- uncomment this and the problem goes away |
Note that this only reproduces if some exceptions are uncaught (the |
I think it's some kind of heisenbug. I worked around the lineno being None in linecache.py:31, then it failed on None in another place later on, so I added a "print the frame" just before that line, and the problem went away. |
It looks like we're sending the trace function a "return" event, when we're actually in the process of raising the rest of the EG. That's wrong... |
For example, in my example above, if you uncomment the So it's not that the |
As far as I can tell the lineno=-1 is on the frame for the "raise ExceptionGroup" line. The except* machienery doesn't do anything with tracebacks/frames other than shallow-copy tracebacks from the original exception group to the parts. |
Can you reproduce this scenario with a normal exception re-raised in an except?
|
Ah wait, it's okay to send a return event. I forgot that raising still traces a return like that. |
def f():
try:
raise ExceptionGroup("eg", [KeyError(), ValueError()])
except* KeyError:
breakpoint() >>> dis.dis(f)
1 0 RESUME 0
2 2 NOP
3 4 LOAD_GLOBAL 1 (NULL + ExceptionGroup)
16 LOAD_CONST 1 ('eg')
18 LOAD_GLOBAL 3 (NULL + KeyError)
30 CALL 0
40 LOAD_GLOBAL 5 (NULL + ValueError)
52 CALL 0
62 BUILD_LIST 2
64 CALL 2
74 RAISE_VARARGS 1
>> 76 PUSH_EXC_INFO
4 78 COPY 1
80 BUILD_LIST 0
82 SWAP 2
84 LOAD_GLOBAL 2 (KeyError)
96 CHECK_EG_MATCH
98 COPY 1
100 POP_JUMP_IF_NOT_NONE 2 (to 106)
102 POP_TOP
104 JUMP_FORWARD 16 (to 138)
>> 106 POP_TOP
5 108 LOAD_GLOBAL 7 (NULL + breakpoint)
120 CALL 0
130 POP_TOP
132 JUMP_FORWARD 2 (to 138)
>> 134 LIST_APPEND 3
136 POP_TOP
>> 138 LIST_APPEND 1
140 PREP_RERAISE_STAR
142 COPY 1
144 POP_JUMP_IF_NOT_NONE 4 (to 154)
146 POP_TOP
148 POP_EXCEPT
150 LOAD_CONST 0 (None)
152 RETURN_VALUE
>> 154 SWAP 2
156 POP_EXCEPT
158 RERAISE 0
>> 160 COPY 3
162 POP_EXCEPT
164 RERAISE 1
>>>
list(f.__code__.co_lines())
[(0, 2, 1), (2, 4, 2), (4, 76, 3), (76, 78, None), (78, 108, 4), (108, 134, 5), (134, 146, None), (146, 154, 5), (154, 166, None)] It looks like the The compiler should be propagating the line number from |
As for the unittest itself, will this work? def test_pdb_issue_gh_101517():
"""See GH-101517
Make sure pdb doesn't crash when the exception is caught in a try/except block
>>> def test_function():
... try:
... raise KeyError
... except* Exception as e:
... import pdb; pdb.Pdb(nosigint=True, readrc=False).set_trace()
>>> with PdbTestInput([ # doctest: +NORMALIZE_WHITESPACE
... 'continue'
... ]):
... test_function()
--Return--
> <doctest test.test_pdb.test_pdb_issue_gh_101517[0]>(None)test_function()->None
(Pdb) continue
""" I've tested it on main and it passed. I reverted 366b949 and it failed. Not sure if this is the thing you are looking for. |
Yes, perfect. I tried something similar but I didn't put the try-except inside a function so it didn't trigger the issue. Could you make a PR to commit this test? Just in the comment - call it a Thanks! |
Sure, but we should fix |
I think that's due to a bug in the compiler's code emitted for |
But this whole thing is due to that bug right? Your fix in 366b949 basically prevent I'm all for the new unittest and this should always be added for the I can definitely make a PR for the unittest above right now, but should we consider either:
|
It's not fixed yet, but I have a fix that I want to push. But I don't have a unit test for this bug which doesn't involve pdb, because the bug is due to some virtual instruction not having a line number assigned to them, and I don't know how to make that visible from python without pdb. The breakpoint make pdb look at the "next instruction", and this is what's failing. So your unit test will be the regression test for my fix, and when I push my fix I will make pdb fail when the lineno is None (but with a better error message than what was there before).
Indeed.
I'd go for the second option. We include a unit test for the bug that the user reported in this issue, we fix this problem, and then add the other tests that we discovered are missing as followup work. |
Okay, so I'll check in the regression test above, which is always valid. Then with your fix for the actual problem, you can remove the protection in |
Making bdb/pdb more robust when confronted with corner cases is also good though. |
If However, if |
I'm on the fence on whether 366b949 makes pdb/bdb more robust, or silences a bug. |
How could pdb know it is "wrong"? It can be a real bummer when the debugger itself crashes, so I just want it to keep working as well as it can. Sometimes the debugger is being used to track down bugs in the compiler. |
Sure we can try to make the debugger work even if python is not working properly. I guess we can generate a warning for |
…o get currect line numbers
PR is here: #103550 I didn't use the warnings module, just print like in a few other places in this module (I'm assuming there's a good reason for that). |
* main: Remove `expert-*` from `project-updater` GH workflow (python#103579) pythongh-103583: Add codecs and maps to _codecs_* module state (python#103540) pythongh-48330: address review comments to PR-12271 (python#103209) pythongh-103527: Add multibytecodec.h as make dep for _codecs_* (python#103567) pythongh-103553: Improve `test_inspect`: add more assertions, remove unused (python#103554) pythonGH-103517: Improve tests for `pathlib.Path.walk()` (pythonGH-103518) pythongh-102114: Make dis print more concise tracebacks for syntax errors in str inputs (python#102115) pythonGH-78079: Fix UNC device path root normalization in pathlib (pythonGH-102003) pythongh-101517: Add regression test for a lineno bug in try/except* impacting pdb (python#103547) pythongh-103527: Add make deps for _codecs_* and _multibytecodec (python#103528) pythongh-103532: Fix reST syntax in NEWS entry (pythonGH-103544) pythongh-103532: Add NEWS entry (python#103542)
* superopt: update generated cases with new comment review comments Remove `expert-*` from `project-updater` GH workflow (python#103579) pythongh-103583: Add codecs and maps to _codecs_* module state (python#103540) pythongh-48330: address review comments to PR-12271 (python#103209) pythongh-103527: Add multibytecodec.h as make dep for _codecs_* (python#103567) pythongh-103553: Improve `test_inspect`: add more assertions, remove unused (python#103554) pythonGH-103517: Improve tests for `pathlib.Path.walk()` (pythonGH-103518) pythongh-102114: Make dis print more concise tracebacks for syntax errors in str inputs (python#102115) pythonGH-78079: Fix UNC device path root normalization in pathlib (pythonGH-102003) pythongh-101517: Add regression test for a lineno bug in try/except* impacting pdb (python#103547) pythongh-103527: Add make deps for _codecs_* and _multibytecodec (python#103528) pythongh-103532: Fix reST syntax in NEWS entry (pythonGH-103544) pythongh-103532: Add NEWS entry (python#103542)
* main: pythongh-101517: fix line number propagation in code generated for except* (python#103550) pythongh-103780: Use patch instead of mock in asyncio unix events test (python#103782)
* superopt: (82 commits) pythongh-101517: fix line number propagation in code generated for except* (python#103550) pythongh-103780: Use patch instead of mock in asyncio unix events test (python#103782) pythongh-102498 Clean up unused variables and imports in the email module (python#102482) pythongh-99184: Bypass instance attribute access in `repr` of `weakref.ref` (python#99244) pythongh-99032: datetime docs: Encoding is no longer relevant (python#93365) pythongh-94300: Update datetime.strptime documentation (python#95318) pythongh-103776: Remove explicit uses of $(SHELL) from Makefile (pythonGH-103778) pythongh-87092: fix a few cases of incorrect error handling in compiler (python#103456) pythonGH-103727: Avoid advancing tokenizer too far in f-string mode (pythonGH-103775) Revert "Add tests for empty range equality (python#103751)" (python#103770) pythongh-94518: Port 23-argument `_posixsubprocess.fork_exec` to Argument Clinic (python#94519) pythonGH-65022: Fix description of copyreg.pickle function (python#102656) pythongh-103323: Get the "Current" Thread State from a Thread-Local Variable (pythongh-103324) pythongh-91687: modernize dataclass example typing (python#103773) pythongh-103746: Test `types.UnionType` and `Literal` types together (python#103747) pythongh-103765: Fix 'Warning: py:class reference target not found: ModuleSpec' (pythonGH-103769) pythongh-87452: Improve the Popen.returncode docs Removed unnecessary escaping of asterisks (python#103714) pythonGH-102973: Slim down Fedora packages in the dev container (python#103283) pythongh-103091: Add PyUnstable_Type_AssignVersionTag (python#103095) ...
This was fixed (in 3.11 as well). We will follow up on the more general problem in #103795. |
Bug report
Consider this code:
Run it on Python 3.11 or 3.12:
One of the exceptions has to be a
ZeroDivisionError
for it to happen as far as I can see.Your environment
Linked PRs
The text was updated successfully, but these errors were encountered: