diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index f4a4634fc62f8a..54e69277282c30 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -188,6 +188,68 @@ def foo(x): ] self.assertEqual(traceback_lines, expected_lines) + def test_pythonstartup_error_reporting(self): + # errors based on https://github.com/python/cpython/issues/137576 + + def make_repl(env): + return subprocess.Popen( + [os.path.join(os.path.dirname(sys.executable), ''), "-i"], + executable=sys.executable, + text=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=env, + ) + + # case 1: error in user input, but PYTHONSTARTUP is fine + with os_helper.temp_dir() as tmpdir: + script = os.path.join(tmpdir, "pythonstartup.py") + with open(script, "w") as f: + f.write("print('from pythonstartup')" + os.linesep) + + env = os.environ.copy() + env['PYTHONSTARTUP'] = script + env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".pythonhist") + p = make_repl(env) + p.stdin.write("1/0") + output = kill_python(p) + expected = dedent(""" + Traceback (most recent call last): + File "", line 1, in + 1/0 + ~^~ + ZeroDivisionError: division by zero + """) + self.assertIn("from pythonstartup", output) + self.assertIn(expected, output) + + # case 2: error in PYTHONSTARTUP triggered by user input + with os_helper.temp_dir() as tmpdir: + script = os.path.join(tmpdir, "pythonstartup.py") + with open(script, "w") as f: + f.write("def foo():\n 1/0\n") + + env = os.environ.copy() + env['PYTHONSTARTUP'] = script + env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".pythonhist") + p = make_repl(env) + p.stdin.write('foo()') + output = kill_python(p) + expected = dedent(""" + Traceback (most recent call last): + File "", line 1, in + foo() + ~~~^^ + File "%s", line 2, in foo + 1/0 + ~^~ + ZeroDivisionError: division by zero + """) % script + self.assertIn(expected, output) + + + def test_runsource_show_syntax_error_location(self): user_input = dedent("""def f(x, x): ... """) diff --git a/Lib/traceback.py b/Lib/traceback.py index 318ec13cf91121..1fe295add3a6dd 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -541,7 +541,7 @@ def format_frame_summary(self, frame_summary, **kwargs): colorize = kwargs.get("colorize", False) row = [] filename = frame_summary.filename - if frame_summary.filename.startswith("-"): + if frame_summary.filename.startswith("'): filename = "" if colorize: theme = _colorize.get_theme(force_color=True).traceback diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-10-21-34-12.gh-issue-137576.0ZicS-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-10-21-34-12.gh-issue-137576.0ZicS-.rst new file mode 100644 index 00000000000000..6593e0554ba04d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-10-21-34-12.gh-issue-137576.0ZicS-.rst @@ -0,0 +1,2 @@ +Fix for incorrect source code being shown in tracebacks from the Basic REPL +when ``PYTHONSTARTUP`` is given. Patch by Adam Hartz. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 8f1c78bf831863..5d388f4aa7fadc 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1365,6 +1365,25 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py return PyEval_EvalCode((PyObject*)co, globals, locals); } +static PyObject * +get_interactive_filename(PyObject *filename, Py_ssize_t count){ + PyObject *result; + Py_ssize_t len = PyUnicode_GET_LENGTH(filename); + + if (len >= 2 + && PyUnicode_ReadChar(filename, 0) == '<' + && PyUnicode_ReadChar(filename, len - 1) == '>') { + PyObject *middle = PyUnicode_Substring(filename, 1, len-1); + result = PyUnicode_FromFormat("<%U-%d>", middle, count); + Py_DECREF(middle); + } else { + result = PyUnicode_FromFormat( + "%U-%d", filename, count); + } + return result; + +} + static PyObject * run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, PyCompilerFlags *flags, PyArena *arena, PyObject* interactive_src, @@ -1375,8 +1394,8 @@ run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals, if (interactive_src) { PyInterpreterState *interp = tstate->interp; if (generate_new_source) { - interactive_filename = PyUnicode_FromFormat( - "%U-%d", filename, interp->_interactive_src_count++); + interactive_filename = get_interactive_filename( + filename, interp->_interactive_src_count++); } else { Py_INCREF(interactive_filename); }