[3.14] gh-142737: Handle lost io.open in _Py_FindSourceFile (GH-142747) (GH-142773)

gh-142737: Handle lost `io.open` in `_Py_FindSourceFile` (GH-142747)
(cherry picked from commit f277781bba)

Co-authored-by: Bartosz Sławecki <bartosz@ilikepython.com>
This commit is contained in:
Miss Islington (bot) 2025-12-16 00:30:00 +01:00 committed by GitHub
parent 88ac9956cd
commit b9cbdde80d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 35 additions and 1 deletions

View file

@ -18,7 +18,7 @@
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 @@ def __del__(self):
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(

View file

@ -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.

View 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);

View 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) {