summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2020-05-13 09:56:47 -0700
committerGuido van Rossum <guido@python.org>2020-05-13 12:39:03 -0700
commit87e78b517b22bd5a7e2d55313a67585924c1d80b (patch)
tree0e225f29b07c91bec919885f630ffca3bff33644
parenta15c9b3a0524e5ca0434d2ad11076677824af941 (diff)
downloadcpython-git-87e78b517b22bd5a7e2d55313a67585924c1d80b.tar.gz
bpo-40612: Fix SyntaxError edge cases in traceback module
-rw-r--r--Lib/test/test_traceback.py10
-rw-r--r--Lib/traceback.py14
2 files changed, 14 insertions, 10 deletions
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 7361d091cf..c703cedb05 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -58,13 +58,13 @@ class TracebackCases(unittest.TestCase):
SyntaxError)
self.assertIn("^", err[2]) # third line has caret
self.assertEqual(err[2].count('\n'), 1) # and no additional newline
- self.assertEqual(err[1].find("+"), err[2].find("^")) # in the right place
+ self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
err = self.get_exception_format(self.syntax_error_with_caret_non_ascii,
SyntaxError)
self.assertIn("^", err[2]) # third line has caret
self.assertEqual(err[2].count('\n'), 1) # and no additional newline
- self.assertEqual(err[1].find("+"), err[2].find("^")) # in the right place
+ self.assertEqual(err[1].find("+") + 1, err[2].find("^")) # in the right place
def test_nocaret(self):
exc = SyntaxError("error", ("x.py", 23, None, "bad syntax"))
@@ -78,7 +78,7 @@ class TracebackCases(unittest.TestCase):
self.assertEqual(len(err), 4)
self.assertEqual(err[1].strip(), "print(2)")
self.assertIn("^", err[2])
- self.assertEqual(err[1].find(")"), err[2].find("^"))
+ self.assertEqual(err[1].find(")") + 1, err[2].find("^"))
err = self.get_exception_format(self.syntax_error_bad_indentation2,
IndentationError)
@@ -656,7 +656,7 @@ class BaseExceptionReportingTests:
self.assertIn('inner_raise() # Marker', blocks[2])
self.check_zero_div(blocks[2])
- @support.skip_if_new_parser("Pegen is arguably better here, so no need to fix this")
+ @unittest.skipIf(support.use_old_parser(), "Pegen is arguably better here, so no need to fix this")
def test_syntax_error_offset_at_eol(self):
# See #10186.
def e():
@@ -666,7 +666,7 @@ class BaseExceptionReportingTests:
def e():
exec("x = 5 | 4 |")
msg = self.get_report(e).splitlines()
- self.assertEqual(msg[-2], ' ^')
+ self.assertEqual(msg[-2], ' ^')
def test_message_none(self):
# A message that looks like "None" should not be treated specially
diff --git a/Lib/traceback.py b/Lib/traceback.py
index bf34bbab8a..7d5561769f 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -569,9 +569,12 @@ class TracebackException:
if not issubclass(self.exc_type, SyntaxError):
yield _format_final_exc_line(stype, self._str)
- return
+ else:
+ yield from self._format_syntax_error(stype)
- # It was a syntax error; show exactly where the problem was found.
+ def _format_syntax_error(self, stype):
+ """Format SyntaxError exceptions (internal helper)."""
+ # Show exactly where the problem was found.
filename = self.filename or "<string>"
lineno = str(self.lineno) or '?'
yield ' File "{}", line {}\n'.format(filename, lineno)
@@ -580,12 +583,13 @@ class TracebackException:
offset = self.offset
if badline is not None:
yield ' {}\n'.format(badline.strip())
- if offset is not None:
+ if offset is not None and offset >= 1:
caretspace = badline.rstrip('\n')
- offset = min(len(caretspace), offset) - 1
+ # Convert to 0-based offset, and clip to text length
+ offset = min(len(caretspace), offset - 1)
caretspace = caretspace[:offset].lstrip()
# non-space whitespace (likes tabs) must be kept for alignment
- caretspace = ((c.isspace() and c or ' ') for c in caretspace)
+ caretspace = ((c if c.isspace() else ' ') for c in caretspace)
yield ' {}^\n'.format(''.join(caretspace))
msg = self.msg or "<no detail available>"
yield "{}: {}\n".format(stype, msg)