https://github.com/python/cpython/commit/5131b8fe7ee61928a16c3aa71be91cc3b3d20f5e
commit: 5131b8fe7ee61928a16c3aa71be91cc3b3d20f5e
branch: 3.13
author: adam j hartz <[email protected]>
committer: Eclips4 <[email protected]>
date: 2025-08-15T12:10:39+03:00
summary:

[3.13] gh-137576: Fix for Basic REPL showing incorrect code in tracebacks with 
`PYTHONSTARTUP` (GH-137625) (#137778)

(cherry picked from commit 04f8ef663be7589def9f456a2024e1707e3408ea)

Co-authored-by: Kirill Podoprigora <[email protected]>

files:
A Misc/NEWS.d/next/Core and 
Builtins/2025-08-10-21-34-12.gh-issue-137576.0ZicS-.rst
M Lib/test/test_repl.py
M Lib/traceback.py
M Python/pythonrun.c

diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py
index 228b326699e75f..47e041ef8273a1 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), '<stdin>'), 
"-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 "<stdin>", line 1, in <module>
+                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 "<stdin>", line 1, in <module>
+                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 e70d358ca2be44..572a3177cb0e14 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -528,7 +528,7 @@ def format_frame_summary(self, frame_summary, **kwargs):
         colorize = kwargs.get("colorize", False)
         row = []
         filename = frame_summary.filename
-        if frame_summary.filename.startswith("<stdin>-"):
+        if frame_summary.filename.startswith("<stdin-") and 
frame_summary.filename.endswith('>'):
             filename = "<stdin>"
         if colorize:
             row.append('  File {}"{}"{}, line {}{}{}, in {}{}{}\n'.format(
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..19e0f3bf10e04b
--- /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 :envvar:`PYTHONSTARTUP` is given. Patch by Adam Hartz.
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 62b70931778b1d..c4a1275eb524d6 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -1385,6 +1385,29 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject 
*co, PyObject *globals, Py
     return v;
 }
 
+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);
+        if (middle == NULL) {
+            return NULL;
+        }
+        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,
@@ -1395,8 +1418,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);
         }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to