diff options
author | Sergey B Kirpichev <skirpichev@gmail.com> | 2024-09-03 12:37:29 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-03 12:37:29 (GMT) |
commit | 6822cb23c62032381971d8a47fd41d1e98710a8c (patch) | |
tree | da368dc55e3db0716a81df0c2644985a937e4146 | |
parent | ef9d54703f1f21d2fde2fe3ef49d4cf7c8b38f06 (diff) | |
download | cpython-6822cb23c62032381971d8a47fd41d1e98710a8c.zip cpython-6822cb23c62032381971d8a47fd41d1e98710a8c.tar.gz cpython-6822cb23c62032381971d8a47fd41d1e98710a8c.tar.bz2 |
gh-121804: always show error location for SyntaxError's in basic repl (#123202)
-rw-r--r-- | Lib/test/test_repl.py | 13 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2024-08-21-15-22-53.gh-issue-121804.r5K3PS.rst | 2 | ||||
-rw-r--r-- | Python/pythonrun.c | 31 |
3 files changed, 46 insertions, 0 deletions
diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index 0b93862..363808c 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -187,6 +187,19 @@ class TestInteractiveInterpreter(unittest.TestCase): ] 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 0000000..ce96c31 --- /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 a :exc:`SyntaxError` is raised +in the basic REPL. Patch by Sergey B Kirpichev. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index ce7f194..b675971 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -280,11 +280,42 @@ PyRun_InteractiveOneObjectEx(FILE *fp, PyObject *filename, PyObject *main_dict = PyModule_GetDict(main_module); // borrowed ref PyObject *res = run_mod(mod, filename, main_dict, main_dict, flags, arena, interactive_src, 1); + Py_INCREF(interactive_src); _PyArena_Free(arena); Py_DECREF(main_module); if (res == NULL) { + PyThreadState *tstate = _PyThreadState_GET(); + PyObject *exc = _PyErr_GetRaisedException(tstate); + if (PyType_IsSubtype(Py_TYPE(exc), + (PyTypeObject *) PyExc_SyntaxError)) + { + /* fix "text" attribute */ + assert(interactive_src != NULL); + PyObject *xs = PyUnicode_Splitlines(interactive_src, 1); + if (xs == NULL) { + goto error; + } + PyObject *exc_lineno = PyObject_GetAttr(exc, &_Py_ID(lineno)); + if (exc_lineno == NULL) { + Py_DECREF(xs); + goto error; + } + int n = PyLong_AsInt(exc_lineno); + Py_DECREF(exc_lineno); + if (n <= 0 || n > PyList_GET_SIZE(xs)) { + Py_DECREF(xs); + goto error; + } + PyObject *line = PyList_GET_ITEM(xs, n - 1); + PyObject_SetAttr(exc, &_Py_ID(text), line); + Py_DECREF(xs); + } +error: + Py_DECREF(interactive_src); + _PyErr_SetRaisedException(tstate, exc); return -1; } + Py_DECREF(interactive_src); Py_DECREF(res); flush_io(); |