Skip to content

Commit 04f8ef6

Browse files
adqmEclips4
andauthored
gh-137576: Fix for Basic REPL showing incorrect code in tracebacks with PYTHONSTARTUP (#137625)
Co-authored-by: Kirill Podoprigora <kirill.bast9@mail.ru>
1 parent 8b8bd3d commit 04f8ef6

File tree

4 files changed

+90
-3
lines changed

4 files changed

+90
-3
lines changed

Lib/test/test_repl.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,68 @@ def foo(x):
188188
]
189189
self.assertEqual(traceback_lines, expected_lines)
190190

191+
def test_pythonstartup_error_reporting(self):
192+
# errors based on https://github.com/python/cpython/issues/137576
193+
194+
def make_repl(env):
195+
return subprocess.Popen(
196+
[os.path.join(os.path.dirname(sys.executable), '<stdin>'), "-i"],
197+
executable=sys.executable,
198+
text=True,
199+
stdin=subprocess.PIPE,
200+
stdout=subprocess.PIPE,
201+
stderr=subprocess.STDOUT,
202+
env=env,
203+
)
204+
205+
# case 1: error in user input, but PYTHONSTARTUP is fine
206+
with os_helper.temp_dir() as tmpdir:
207+
script = os.path.join(tmpdir, "pythonstartup.py")
208+
with open(script, "w") as f:
209+
f.write("print('from pythonstartup')" + os.linesep)
210+
211+
env = os.environ.copy()
212+
env['PYTHONSTARTUP'] = script
213+
env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".pythonhist")
214+
p = make_repl(env)
215+
p.stdin.write("1/0")
216+
output = kill_python(p)
217+
expected = dedent("""
218+
Traceback (most recent call last):
219+
File "<stdin>", line 1, in <module>
220+
1/0
221+
~^~
222+
ZeroDivisionError: division by zero
223+
""")
224+
self.assertIn("from pythonstartup", output)
225+
self.assertIn(expected, output)
226+
227+
# case 2: error in PYTHONSTARTUP triggered by user input
228+
with os_helper.temp_dir() as tmpdir:
229+
script = os.path.join(tmpdir, "pythonstartup.py")
230+
with open(script, "w") as f:
231+
f.write("def foo():\n 1/0\n")
232+
233+
env = os.environ.copy()
234+
env['PYTHONSTARTUP'] = script
235+
env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".pythonhist")
236+
p = make_repl(env)
237+
p.stdin.write('foo()')
238+
output = kill_python(p)
239+
expected = dedent("""
240+
Traceback (most recent call last):
241+
File "<stdin>", line 1, in <module>
242+
foo()
243+
~~~^^
244+
File "%s", line 2, in foo
245+
1/0
246+
~^~
247+
ZeroDivisionError: division by zero
248+
""") % script
249+
self.assertIn(expected, output)
250+
251+
252+
191253
def test_runsource_show_syntax_error_location(self):
192254
user_input = dedent("""def f(x, x): ...
193255
""")

Lib/traceback.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ def format_frame_summary(self, frame_summary, **kwargs):
541541
colorize = kwargs.get("colorize", False)
542542
row = []
543543
filename = frame_summary.filename
544-
if frame_summary.filename.startswith("<stdin>-"):
544+
if frame_summary.filename.startswith("<stdin-") and frame_summary.filename.endswith('>'):
545545
filename = "<stdin>"
546546
if colorize:
547547
theme = _colorize.get_theme(force_color=True).traceback
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix for incorrect source code being shown in tracebacks from the Basic REPL
2+
when :envvar:`PYTHONSTARTUP` is given. Patch by Adam Hartz.

Python/pythonrun.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,29 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py
13651365
return PyEval_EvalCode((PyObject*)co, globals, locals);
13661366
}
13671367

1368+
static PyObject *
1369+
get_interactive_filename(PyObject *filename, Py_ssize_t count)
1370+
{
1371+
PyObject *result;
1372+
Py_ssize_t len = PyUnicode_GET_LENGTH(filename);
1373+
1374+
if (len >= 2
1375+
&& PyUnicode_ReadChar(filename, 0) == '<'
1376+
&& PyUnicode_ReadChar(filename, len - 1) == '>') {
1377+
PyObject *middle = PyUnicode_Substring(filename, 1, len-1);
1378+
if (middle == NULL) {
1379+
return NULL;
1380+
}
1381+
result = PyUnicode_FromFormat("<%U-%d>", middle, count);
1382+
Py_DECREF(middle);
1383+
} else {
1384+
result = PyUnicode_FromFormat(
1385+
"%U-%d", filename, count);
1386+
}
1387+
return result;
1388+
1389+
}
1390+
13681391
static PyObject *
13691392
run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals,
13701393
PyCompilerFlags *flags, PyArena *arena, PyObject* interactive_src,
@@ -1375,8 +1398,8 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals,
13751398
if (interactive_src) {
13761399
PyInterpreterState *interp = tstate->interp;
13771400
if (generate_new_source) {
1378-
interactive_filename = PyUnicode_FromFormat(
1379-
"%U-%d", filename, interp->_interactive_src_count++);
1401+
interactive_filename = get_interactive_filename(
1402+
filename, interp->_interactive_src_count++);
13801403
} else {
13811404
Py_INCREF(interactive_filename);
13821405
}

0 commit comments

Comments
 (0)