From: Bartosz Sławecki Date: Mon, 15 Dec 2025 23:27:15 +0000 (+0100) Subject: [3.13] gh-142737: Handle lost `io.open` in `_Py_FindSourceFile` (GH-142747) (GH-142774) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3e45440382e47da7d27d7b15552139b617382062;p=thirdparty%2FPython%2Fcpython.git [3.13] gh-142737: Handle lost `io.open` in `_Py_FindSourceFile` (GH-142747) (GH-142774) (cherry picked from commit f277781bba684322dffffe45cd878f4652ccf7e4) --- diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 4b5dd3d25657..915055fdc743 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -18,8 +18,8 @@ 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.script_helper import assert_python_ok, assert_python_failure +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 '.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 index 000000000000..8b743d1e49de --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2025-12-15-15-01-21.gh-issue-142737.xYXzeB.rst @@ -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. diff --git a/Objects/call.c b/Objects/call.c index b1610dababd4..6e331a43899a 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -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); diff --git a/Python/traceback.c b/Python/traceback.c index f98408337164..81960a769271 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -356,6 +356,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) {