Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -524,6 +524,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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
1 change: 1 addition & 0 deletions Objects/call.c
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ _Py_COMP_DIAG_POP
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);
Expand Down
3 changes: 3 additions & 0 deletions Python/traceback.c
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,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) {
Expand Down
Loading