Visitar URL original
bpo-45249: Add regression test for display of SyntaxError range indicator in doctests by akulakov · Pull Request #28567 · python/cpython · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
add _format_syntax_error() to doctest, add unit test for it
  • Loading branch information
akulakov committed Sep 26, 2021
commit a5271379a0ef16a3d0432407566fce628369ec63
41 changes: 40 additions & 1 deletion Lib/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def _test():
import re
import sys
import traceback
import types
import unittest
from io import StringIO, IncrementalNewlineDecoder
from collections import namedtuple
Expand Down Expand Up @@ -241,6 +242,44 @@ def _indent(s, indent=4):
# This regexp matches the start of non-blank lines:
return re.sub('(?m)^(?!$)', indent*' ', s)

def _traceback_format_syntax_error(self, stype):
"""Format SyntaxError exceptions (internal helper). Copied from
traceback.py
"""
# Show exactly where the problem was found.
filename_suffix = ''
if self.lineno is not None:
yield ' File "{}", line {}\n'.format(
self.filename or "<string>", self.lineno)
elif self.filename is not None:
filename_suffix = ' ({})'.format(self.filename)

text = self.text
if text is not None:
# text = " foo\n"
# rtext = " foo"
# ltext = "foo"
rtext = text.rstrip('\n')
ltext = rtext.lstrip(' \n\f')
spaces = len(rtext) - len(ltext)
yield ' {}\n'.format(ltext)
# Convert 1-based column offset to 0-based index into stripped text
caret = (self.offset or 0) - 1 - spaces
if caret >= 0:
# non-space whitespace (likes tabs) must be kept for alignment
caretspace = ((c if c.isspace() else ' ') for c in ltext[:caret])
yield ' {}{}\n'.format(''.join(caretspace), '^'*(self.exc_value.end_offset-self.offset))
msg = self.msg or "<no detail available>"
yield "{}: {}{}\n".format(stype, msg, filename_suffix)

def _print_exception(exc, /, value, tb, limit=None, file=None, chain=True):
"""Copied from traceback.py"""
value, tb = traceback._parse_value_tb(exc, value, tb)
te = traceback.TracebackException(type(value), value, tb, limit=limit, compact=True)
te._format_syntax_error = types.MethodType(_traceback_format_syntax_error, te)
te.exc_value = value
te.print(file=file, chain=chain)

def _exception_traceback(exc_info):
"""
Return a string containing a traceback message for the given
Expand All @@ -249,7 +288,7 @@ def _exception_traceback(exc_info):
# Get a traceback message.
excout = StringIO()
exc_type, exc_val, exc_tb = exc_info
traceback.print_exception(exc_type, exc_val, exc_tb, file=excout)
_print_exception(exc_type, exc_val, exc_tb, file=excout)
return excout.getvalue()

# Override some StringIO methods.
Expand Down
21 changes: 21 additions & 0 deletions Lib/test/test_doctest3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import unittest
import io
import contextlib
import doctest

class Test(unittest.TestCase):
def test_syntax_error(self):
f = io.StringIO()
with contextlib.redirect_stdout(f):
try:
doctest.run_docstring_examples("""
>>> 1 1
""", globals())
except Exception as e:
print('!!!!!', str(e))
self.assertIn('''
1 1
^^^
SyntaxError: invalid syntax. Perhaps you forgot a comma?''',
f.getvalue())

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fixed display of :exc:`SyntaxError` errors in :mod:`doctest` to show full
error range indicator rather than a single caret (``^``).