Skip to content

Commit 7dd8f83

Browse files
committed
pythongh-121804: always show error location for SyntaxError's in basic repl
1 parent 77133f5 commit 7dd8f83

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-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 :exc:`SyntaxError` raised in basic
2+
repl. Patch by Sergey B Kirpichev.

Python/pythonrun.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,34 @@ PyRun_InteractiveOneObjectEx(FILE *fp, PyObject *filename,
283283
_PyArena_Free(arena);
284284
Py_DECREF(main_module);
285285
if (res == NULL) {
286+
PyThreadState *tstate = _PyThreadState_GET();
287+
PyObject *exc = tstate->current_exception;
288+
if ((PyObject *)Py_TYPE(exc) == PyExc_SyntaxError) {
289+
/* fix "text" attribute */
290+
assert(interactive_src);
291+
PyObject *xs = PyUnicode_Splitlines(interactive_src, 1);
292+
if (xs == NULL) {
293+
return -1;
294+
}
295+
PyObject *ln = PyObject_GetAttr(exc, &_Py_ID(lineno));
296+
if (ln == NULL) {
297+
Py_DECREF(xs);
298+
return -1;
299+
}
300+
int n = PyLong_AsInt(ln);
301+
Py_DECREF(ln);
302+
assert(n>0);
303+
assert(PyList_GET_SIZE(xs)>=n);
304+
PyObject *line = PyList_GET_ITEM(xs, n - 1);
305+
Py_INCREF(line);
306+
Py_DECREF(xs);
307+
if (PyObject_SetAttr(exc, &_Py_ID(text), line) == -1) {
308+
Py_DECREF(line);
309+
_PyErr_Clear(tstate);
310+
return -1;
311+
}
312+
Py_DECREF(line);
313+
}
286314
return -1;
287315
}
288316
Py_DECREF(res);

0 commit comments

Comments
 (0)