Skip to content

Commit 87e78b5

Browse files
committed
bpo-40612: Fix SyntaxError edge cases in traceback module
1 parent a15c9b3 commit 87e78b5

File tree

2 files changed

+14
-10
lines changed

2 files changed

+14
-10
lines changed

Lib/test/test_traceback.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ def test_caret(self):
5858
SyntaxError)
5959
self.assertIn("^", err[2]) # third line has caret
6060
self.assertEqual(err[2].count('\n'), 1) # and no additional newline
61-
self.assertEqual(err[1].find("+"), err[2].find("^")) # in the right place
61+
self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
6262

6363
err = self.get_exception_format(self.syntax_error_with_caret_non_ascii,
6464
SyntaxError)
6565
self.assertIn("^", err[2]) # third line has caret
6666
self.assertEqual(err[2].count('\n'), 1) # and no additional newline
67-
self.assertEqual(err[1].find("+"), err[2].find("^")) # in the right place
67+
self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
6868

6969
def test_nocaret(self):
7070
exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
@@ -78,7 +78,7 @@ def test_bad_indentation(self):
7878
self.assertEqual(len(err), 4)
7979
self.assertEqual(err[1].strip(), "print(2)")
8080
self.assertIn("^", err[2])
81-
self.assertEqual(err[1].find(")"), err[2].find("^"))
81+
self.assertEqual(err[1].find(")") + 1, err[2].find("^"))
8282

8383
err = self.get_exception_format(self.syntax_error_bad_indentation2,
8484
IndentationError)
@@ -656,7 +656,7 @@ def outer_raise():
656656
self.assertIn('inner_raise() # Marker', blocks[2])
657657
self.check_zero_div(blocks[2])
658658

659-
@support.skip_if_new_parser("Pegen is arguably better here, so no need to fix this")
659+
@unittest.skipIf(support.use_old_parser(), "Pegen is arguably better here, so no need to fix this")
660660
def test_syntax_error_offset_at_eol(self):
661661
# See #10186.
662662
def e():
@@ -666,7 +666,7 @@ def e():
666666
def e():
667667
exec("x = 5 | 4 |")
668668
msg = self.get_report(e).splitlines()
669-
self.assertEqual(msg[-2], ' ^')
669+
self.assertEqual(msg[-2], ' ^')
670670

671671
def test_message_none(self):
672672
# A message that looks like "None" should not be treated specially

Lib/traceback.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,9 +569,12 @@ def format_exception_only(self):
569569

570570
if not issubclass(self.exc_type, SyntaxError):
571571
yield _format_final_exc_line(stype, self._str)
572-
return
572+
else:
573+
yield from self._format_syntax_error(stype)
573574

574-
# It was a syntax error; show exactly where the problem was found.
575+
def _format_syntax_error(self, stype):
576+
"""Format SyntaxError exceptions (internal helper)."""
577+
# Show exactly where the problem was found.
575578
filename = self.filename or "<string>"
576579
lineno = str(self.lineno) or '?'
577580
yield ' File "{}", line {}\n'.format(filename, lineno)
@@ -580,12 +583,13 @@ def format_exception_only(self):
580583
offset = self.offset
581584
if badline is not None:
582585
yield ' {}\n'.format(badline.strip())
583-
if offset is not None:
586+
if offset is not None and offset >= 1:
584587
caretspace = badline.rstrip('\n')
585-
offset = min(len(caretspace), offset) - 1
588+
# Convert to 0-based offset, and clip to text length
589+
offset = min(len(caretspace), offset - 1)
586590
caretspace = caretspace[:offset].lstrip()
587591
# non-space whitespace (likes tabs) must be kept for alignment
588-
caretspace = ((c.isspace() and c or ' ') for c in caretspace)
592+
caretspace = ((c if c.isspace() else ' ') for c in caretspace)
589593
yield ' {}^\n'.format(''.join(caretspace))
590594
msg = self.msg or "<no detail available>"
591595
yield "{}: {}\n".format(stype, msg)

0 commit comments

Comments
 (0)