Skip to content

Commit 4894139

Browse files
[3.13] gh-121804: always show error location for SyntaxError's in basic repl (GH-123202) (#123631)
gh-121804: always show error location for SyntaxError's in basic repl (GH-123202) (cherry picked from commit 6822cb2) Co-authored-by: Sergey B Kirpichev <[email protected]>
1 parent d655c65 commit 4894139

File tree

3 files changed

+46
-0
lines changed

3 files changed

+46
-0
lines changed

Lib/test/test_repl.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,19 @@ def foo(x):
187187
]
188188
self.assertEqual(traceback_lines, expected_lines)
189189

190+
def test_runsource_show_syntax_error_location(self):
191+
user_input = dedent("""def f(x, x): ...
192+
""")
193+
p = spawn_repl()
194+
p.stdin.write(user_input)
195+
output = kill_python(p)
196+
expected_lines = [
197+
' def f(x, x): ...',
198+
' ^',
199+
"SyntaxError: duplicate argument 'x' in function definition"
200+
]
201+
self.assertEqual(output.splitlines()[4:-1], expected_lines)
202+
190203
def test_interactive_source_is_in_linecache(self):
191204
user_input = dedent("""
192205
def foo(x):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Correctly show error locations when a :exc:`SyntaxError` is raised
2+
in the basic REPL. Patch by Sergey B Kirpichev.

Python/pythonrun.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,42 @@ PyRun_InteractiveOneObjectEx(FILE *fp, PyObject *filename,
280280
PyObject *main_dict = PyModule_GetDict(main_module); // borrowed ref
281281

282282
PyObject *res = run_mod(mod, filename, main_dict, main_dict, flags, arena, interactive_src, 1);
283+
Py_INCREF(interactive_src);
283284
_PyArena_Free(arena);
284285
Py_DECREF(main_module);
285286
if (res == NULL) {
287+
PyThreadState *tstate = _PyThreadState_GET();
288+
PyObject *exc = _PyErr_GetRaisedException(tstate);
289+
if (PyType_IsSubtype(Py_TYPE(exc),
290+
(PyTypeObject *) PyExc_SyntaxError))
291+
{
292+
/* fix "text" attribute */
293+
assert(interactive_src != NULL);
294+
PyObject *xs = PyUnicode_Splitlines(interactive_src, 1);
295+
if (xs == NULL) {
296+
goto error;
297+
}
298+
PyObject *exc_lineno = PyObject_GetAttr(exc, &_Py_ID(lineno));
299+
if (exc_lineno == NULL) {
300+
Py_DECREF(xs);
301+
goto error;
302+
}
303+
int n = PyLong_AsInt(exc_lineno);
304+
Py_DECREF(exc_lineno);
305+
if (n <= 0 || n > PyList_GET_SIZE(xs)) {
306+
Py_DECREF(xs);
307+
goto error;
308+
}
309+
PyObject *line = PyList_GET_ITEM(xs, n - 1);
310+
PyObject_SetAttr(exc, &_Py_ID(text), line);
311+
Py_DECREF(xs);
312+
}
313+
error:
314+
Py_DECREF(interactive_src);
315+
_PyErr_SetRaisedException(tstate, exc);
286316
return -1;
287317
}
318+
Py_DECREF(interactive_src);
288319
Py_DECREF(res);
289320

290321
flush_io();

0 commit comments

Comments
 (0)