diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index 0b938623856e4f..363808cb444322 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -187,6 +187,19 @@ def foo(x): ] self.assertEqual(traceback_lines, expected_lines) + def test_runsource_show_syntax_error_location(self): + user_input = dedent("""def f(x, x): ... + """) + p = spawn_repl() + p.stdin.write(user_input) + output = kill_python(p) + expected_lines = [ + ' def f(x, x): ...', + ' ^', + "SyntaxError: duplicate argument 'x' in function definition" + ] + self.assertEqual(output.splitlines()[4:-1], expected_lines) + def test_interactive_source_is_in_linecache(self): user_input = dedent(""" def foo(x): diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-08-21-15-22-53.gh-issue-121804.r5K3PS.rst b/Misc/NEWS.d/next/Core and Builtins/2024-08-21-15-22-53.gh-issue-121804.r5K3PS.rst new file mode 100644 index 00000000000000..7c9c0230cdd9e5 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-08-21-15-22-53.gh-issue-121804.r5K3PS.rst @@ -0,0 +1,2 @@ +Correctly show error locations, when :exc:`SyntaxError` raised in basic +repl. Patch by Sergey B Kirpichev. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ce7f194e929c9c..a7d577bcd62621 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -283,6 +283,34 @@ PyRun_InteractiveOneObjectEx(FILE *fp, PyObject *filename, _PyArena_Free(arena); Py_DECREF(main_module); if (res == NULL) { + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *exc = tstate->current_exception; + if ((PyObject *)Py_TYPE(exc) == PyExc_SyntaxError) { + /* fix "text" attribute */ + assert(interactive_src); + PyObject *xs = PyUnicode_Splitlines(interactive_src, 1); + if (xs == NULL) { + return -1; + } + PyObject *ln = PyObject_GetAttr(exc, &_Py_ID(lineno)); + if (ln == NULL) { + Py_DECREF(xs); + return -1; + } + int n = PyLong_AsInt(ln); + Py_DECREF(ln); + assert(n>0); + assert(PyList_GET_SIZE(xs)>=n); + PyObject *line = PyList_GET_ITEM(xs, n - 1); + Py_INCREF(line); + Py_DECREF(xs); + if (PyObject_SetAttr(exc, &_Py_ID(text), line) == -1) { + Py_DECREF(line); + _PyErr_Clear(tstate); + return -1; + } + Py_DECREF(line); + } return -1; } Py_DECREF(res);