Skip to content

Commit

Permalink
gh-121804: always show error location for SyntaxError's in new repl
Browse files Browse the repository at this point in the history
>>> def good(x, y): ...
... def bad(x, x): ...
  File "<python-input-13>", line 2
    def bad(x, x): ...
               ^
SyntaxError: duplicate argument 'x' in function definition
  • Loading branch information
skirpichev committed Jul 17, 2024
1 parent bf74db7 commit f5f0d53
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 7 deletions.
10 changes: 5 additions & 5 deletions Lib/_pyrepl/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ def __init__(
super().__init__(locals=locals, filename=filename, local_exit=local_exit) # type: ignore[call-arg]
self.can_colorize = _colorize.can_colorize()

def showsyntaxerror(self, filename=None):
super().showsyntaxerror(colorize=self.can_colorize)
def showsyntaxerror(self, filename=None, **kwargs):
super().showsyntaxerror(colorize=self.can_colorize, **kwargs)

def showtraceback(self):
super().showtraceback(colorize=self.can_colorize)
Expand All @@ -171,7 +171,7 @@ def runsource(self, source, filename="<input>", symbol="single"):
try:
tree = ast.parse(source)
except (SyntaxError, OverflowError, ValueError):
self.showsyntaxerror(filename)
self.showsyntaxerror(filename, source=source)
return False
if tree.body:
*_, last_stmt = tree.body
Expand All @@ -188,10 +188,10 @@ def runsource(self, source, filename="<input>", symbol="single"):
f"Try the asyncio REPL ({python} -m asyncio) to use"
f" top-level 'await' and run background asyncio tasks."
)
self.showsyntaxerror(filename)
self.showsyntaxerror(filename, source=source)
return False
except (OverflowError, ValueError):
self.showsyntaxerror(filename)
self.showsyntaxerror(filename, source=source)
return False

if code is None:
Expand Down
6 changes: 5 additions & 1 deletion Lib/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def runsource(self, source, filename="<input>", symbol="single"):
code = self.compile(source, filename, symbol)
except (OverflowError, SyntaxError, ValueError):
# Case 1
self.showsyntaxerror(filename)
self.showsyntaxerror(filename, source=source)
return False

if code is None:
Expand Down Expand Up @@ -123,6 +123,10 @@ def showsyntaxerror(self, filename=None, **kwargs):
# Stuff in the right filename
value = SyntaxError(msg, (filename, lineno, offset, line))
sys.last_exc = sys.last_value = value
source = kwargs.pop('source', "")
if source and not value.text and type is SyntaxError:
# Set the line of text that the exception refers to
value.text = source.splitlines()[value.lineno - 1]
if sys.excepthook is sys.__excepthook__:
lines = traceback.format_exception_only(type, value, colorize=colorize)
self.write(''.join(lines))
Expand Down
2 changes: 1 addition & 1 deletion Lib/idlelib/pyshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ def prepend_syspath(self, filename):
del _filename, _sys, _dirname, _dir
\n""".format(filename))

def showsyntaxerror(self, filename=None):
def showsyntaxerror(self, filename=None, **kwargs):
"""Override Interactive Interpreter method: Use Colorizing
Color the offending position instead of printing it and pointing at it
Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_pyrepl/test_interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,20 @@ def test_runsource_returns_false_for_failed_compilation(self):
self.assertFalse(result)
self.assertIn('SyntaxError', f.getvalue())

@force_not_colorized
def test_runsource_show_syntax_error_location(self):
console = InteractiveColoredConsole()
source = "def f(x, x): ..."
f = io.StringIO()
with contextlib.redirect_stderr(f):
result = console.runsource(source)
self.assertFalse(result)
r = """
def f(x, x): ...
^
SyntaxError: duplicate argument 'x' in function definition"""
self.assertIn(r, f.getvalue())

def test_runsource_shows_syntax_error_for_failed_compilation(self):
console = InteractiveColoredConsole()
source = "print('Hello, world!'"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add new kwarg ``source`` for
:meth:`InteractiveInterpreter.showsyntaxerror()` to support enhanced error
messages, including error locations. Patch by Sergey B Kirpichev.

0 comments on commit f5f0d53

Please sign in to comment.