Fix SyntaxError crash line to show file, line, and column - #14899
Fix SyntaxError crash line to show file, line, and column#14899SemTiOne wants to merge 13 commits into
Conversation
Co-authored-by: Claude <noreply@anthropic.com>
37b18b6 to
427a995
Compare
…/SemTiOne/pytest into fix-2388-syntax-error-crash-line
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
|
Hello @RonnyPfannschmidt, could you please review this PR when you have a moment? Thank you for your time. |
| else: | ||
| column = None |
There was a problem hiding this comment.
Move path = self._makepath(entry_path) from above to here:
| else: | |
| column = None | |
| else: | |
| path = self._makepath(entry_path) | |
| column = None |
There was a problem hiding this comment.
Hmmm this is good. But when I think about it, is this a bandaid? Because if _syntax_error_location is ever relaxed to return loc even when exc.filename is falsy, then filename or path would evaluate the undefined path, and immediately raise a NameError? Yes mine is also a bandaid I believe, the or path is a dead code, should be deleted and be like this: self._makepath(filename)
There was a problem hiding this comment.
then filename or path would evaluate the undefined path, and immediately raise a NameError?
Not sure I follow... if _syntax_error_location changes, mypy should catch any discrepancies.
There was a problem hiding this comment.
Not sure I follow... if
_syntax_error_locationchanges, mypy should catch any discrepancies.
Ok fair enough.
the
or pathis a dead code, should be deleted and be like this:self._makepath(filename)
What do you think about this?
There was a problem hiding this comment.
What do you think about this?
Let's leave it for now.
| repr_ = excinfo.getrepr(style="short") | ||
| reprcrash = excinfo._getreprcrash() | ||
| msg = str(repr_) | ||
| if reprcrash is not None and reprcrash.column is not None: |
There was a problem hiding this comment.
Why the reprcrash.column guard? If not None, we call str(reprcrash) anyway.
There was a problem hiding this comment.
You are right. str(reprcrash) is safe with column=None, but the guard isn't about safety. Dropping the guard here would append a near-duplicate location line in the no-column case. The column is the signal that we have the SyntaxError's own precise location worth appending, without it we keep the existing traceback line. What do you think?
There was a problem hiding this comment.
We should make the intent more explicit then, preferably without just slapping a comment there... 🤔
There was a problem hiding this comment.
Of course, here's my code proposal, wdyt?
msg = str(repr_)
syntax_error_has_precise_location = reprcrash is not None and reprcrash.column is not None
if syntax_error_has_precise_location:
msg += "\n" + str(reprcrash) | f() | ||
| assert excinfo._getreprcrash() is None | ||
|
|
||
| def test_getreprcrash_syntax_error(self): |
There was a problem hiding this comment.
Most of the tests are constructing a SyntexError manually... can we construct them instead via actual code and exec()? Seems more resilient/reliable that way.
| assert repr.reprcrash.message == "SyntaxError: bad syntax" | ||
| assert str(repr.reprcrash) == "file.py:1:5: SyntaxError: bad syntax" | ||
|
|
||
| def test_syntax_error_default_tb_long(self, pytester: Pytester) -> None: |
There was a problem hiding this comment.
Why are we raising manual SyntaxErrors here, instead of just writing code with syntax errors?
Co-authored-by: Bruno Oliveira <bruno@soliv.dev>
|
Thanks for picking this up — the The Reproducer (validated against the current head, 4d8030b, and against # test_pr14899_repro.py
def helper():
compile("def broken(:", "gen.py", "exec")
def test_issue1_wrong_frame_attribution():
helper()
def test_issue3_chained_cause_link():
try:
compile("def broken(:", "gen.py", "exec")
except SyntaxError as e:
raise RuntimeError("wrapper") from e# test_issue2_broken_syntax.py (a real collection-time SyntaxError)
def broken(:
pass# test_issue7_import_no_offset.py (import-time SyntaxError without offset)
raise SyntaxError("custom import error", ("weird.py", 3, None, "src", 3, None))Issue 1 — frame attribution (
|
SyntaxError crash lines now use the error's own file, line, and column instead of the location of the traceback entry where it was raised.
Before:
After:
The traceback entry location and collection errors use the same
file:line:colformat. TheReprFileLocationdataclass gains an optionalcolumnfield (column: int | None = None); existing output is unchanged when the field is absent.Note: This PR doesn't solve the removal of the now-redundant File..., line N block. This is a deliberate design decision; it comes from stdlib
format_exception_only(); deferred to a follow-up issue.Co-authored-bycommit trailers.changelogdirectory, with a name like<ISSUE NUMBER>.<TYPE>.rst. See changelog/README.rst for details.AUTHORSin alphabetical order.