]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.14] gh-142737: Handle lost `io.open` in `_Py_FindSourceFile` (GH-142747) (GH-142773)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Mon, 15 Dec 2025 23:30:00 +0000 (00:30 +0100)
committerGitHub <noreply@github.com>
Mon, 15 Dec 2025 23:30:00 +0000 (23:30 +0000)
gh-142737: Handle lost `io.open` in `_Py_FindSourceFile` (GH-142747)
(cherry picked from commit f277781bba684322dffffe45cd878f4652ccf7e4)

Co-authored-by: Bartosz Sławecki <bartosz@ilikepython.com>
Lib/test/test_traceback.py
Misc/NEWS.d/next/Core_and_Builtins/2025-12-15-15-01-21.gh-issue-142737.xYXzeB.rst [new file with mode: 0644]
Objects/call.c
Python/traceback.c

index b21daeb1b6755694275428d37a8341d54eff88b5..c87ad67ed05a3a4c0bedfa712392819a871026c5 100644 (file)
@@ -18,7 +18,7 @@ import shutil
 from test.support import (Error, captured_output, cpython_only, ALWAYS_EQ,
                           requires_debug_ranges, has_no_debug_ranges,
                           requires_subprocess)
-from test.support.os_helper import TESTFN, unlink
+from test.support.os_helper import TESTFN, temp_dir, unlink
 from test.support.script_helper import assert_python_ok, assert_python_failure, make_script
 from test.support.import_helper import forget
 from test.support import force_not_colorized, force_not_colorized_test_class
@@ -504,6 +504,33 @@ class TracebackCases(unittest.TestCase):
                     b'ZeroDivisionError: division by zero']
         self.assertEqual(stderr.splitlines(), expected)
 
+    @cpython_only
+    def test_lost_io_open(self):
+        # GH-142737: Display the traceback even if io.open is lost
+        crasher = textwrap.dedent("""\
+            import io
+            import traceback
+            # Trigger fallback mode
+            traceback._print_exception_bltin = None
+            del io.open
+            raise RuntimeError("should not crash")
+        """)
+
+        # Create a temporary script to exercise _Py_FindSourceFile
+        with temp_dir() as script_dir:
+            script = make_script(
+                script_dir=script_dir,
+                script_basename='tb_test_no_io_open',
+                source=crasher)
+            rc, stdout, stderr = assert_python_failure(script)
+
+        self.assertEqual(rc, 1)  # Make sure it's not a crash
+
+        expected = [b'Traceback (most recent call last):',
+                    f'  File "{script}", line 6, in <module>'.encode(),
+                    b'RuntimeError: should not crash']
+        self.assertEqual(stderr.splitlines(), expected)
+
     def test_print_exception(self):
         output = StringIO()
         traceback.print_exception(
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-15-15-01-21.gh-issue-142737.xYXzeB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-15-15-01-21.gh-issue-142737.xYXzeB.rst
new file mode 100644 (file)
index 0000000..8b743d1
--- /dev/null
@@ -0,0 +1,3 @@
+Tracebacks will be displayed in fallback mode even if :func:`io.open` is lost.
+Previously, this would crash the interpreter.
+Patch by Bartosz SÅ‚awecki.
index b1610dababd46605c3aa6ef330dbb6e4d3fed60f..6e331a43899a068b9053fd2d247aa180c776f87d 100644 (file)
@@ -726,6 +726,7 @@ _PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name,
 PyObject * _PyObject_CallMethodFormat(PyThreadState *tstate, PyObject *callable,
                                       const char *format, ...)
 {
+    assert(callable != NULL);
     va_list va;
     va_start(va, format);
     PyObject *retval = callmethod(tstate, callable, format, va);
index 86273da3c8ae3ccbf803888c8be7eda4ae86a90e..b9c9132c0c50c1ec6de93ab2beba8263e7ff3ef2 100644 (file)
@@ -416,6 +416,9 @@ _Py_FindSourceFile(PyObject *filename, char* namebuf, size_t namelen, PyObject *
     npath = PyList_Size(syspath);
 
     open = PyObject_GetAttr(io, &_Py_ID(open));
+    if (open == NULL) {
+        goto error;
+    }
     for (i = 0; i < npath; i++) {
         v = PyList_GetItem(syspath, i);
         if (v == NULL) {