From cf8b9c3f4508df61cff7bf84109abc6e91979856 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 13:53:22 +0100 Subject: [PATCH 01/19] Check `callable` isn't `NULL` in `_PyObject_CallMethodFormat` --- Objects/call.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Objects/call.c b/Objects/call.c index 41d075caf11ce6..c7a36445985444 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -729,6 +729,9 @@ _Py_COMP_DIAG_POP PyObject * _PyObject_CallMethodFormat(PyThreadState *tstate, PyObject *callable, const char *format, ...) { + if (callable == NULL) { + return NULL; + } va_list va; va_start(va, format); PyObject *retval = callmethod(tstate, callable, format, va); From 1cb2a7e4fd97d59605a93fc73eda3bc5968e241d Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 14:17:29 +0100 Subject: [PATCH 02/19] Add initial test idea --- Lib/test/test_traceback.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 259f70f1ea0dbc..eb6556b9152943 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2268,7 +2268,7 @@ class TestTracebackFormat(unittest.TestCase, TracebackFormatMixin): @cpython_only @force_not_colorized_test_class -class TestFallbackTracebackFormat(unittest.TestCase, TracebackFormatMixin): +class TestFallbackTracebackFormat1(unittest.TestCase, TracebackFormatMixin): DEBUG_RANGES = False def setUp(self) -> None: self.original_unraisable_hook = sys.unraisablehook @@ -2282,6 +2282,22 @@ def tearDown(self) -> None: sys.unraisablehook = self.original_unraisable_hook return super().tearDown() +@cpython_only +@force_not_colorized_test_class +class TestFallbackTracebackFormat2(unittest.TestCase, TracebackFormatMixin): + def setUp(self) -> None: + import io + self.original_io = io + self.original_hook = traceback._print_exception_bltin + traceback._print_exception_bltin = object() + sys.modules['io'] = types.SimpleNamespace(StringIO=io.StringIO) + return super().setUp() + + def tearDown(self) -> None: + sys.modules['io'] = self.original_io + traceback._print_exception_bltin = self.original_hook + return super().tearDown() + class BaseExceptionReportingTests: def get_exception(self, exception_or_callable): From e5c77a0e75fda207d84b844f8336a7bd3519a690 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 14:36:14 +0100 Subject: [PATCH 03/19] Give test cases meaningful names --- Lib/test/test_traceback.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index eb6556b9152943..2b5efdd0584aa9 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2268,7 +2268,7 @@ class TestTracebackFormat(unittest.TestCase, TracebackFormatMixin): @cpython_only @force_not_colorized_test_class -class TestFallbackTracebackFormat1(unittest.TestCase, TracebackFormatMixin): +class TestFallbackFormatWhenPrinterRaises(unittest.TestCase, TracebackFormatMixin): DEBUG_RANGES = False def setUp(self) -> None: self.original_unraisable_hook = sys.unraisablehook @@ -2284,7 +2284,7 @@ def tearDown(self) -> None: @cpython_only @force_not_colorized_test_class -class TestFallbackTracebackFormat2(unittest.TestCase, TracebackFormatMixin): +class TestFallbackFormatWhenIOUnavailable(unittest.TestCase, TracebackFormatMixin): def setUp(self) -> None: import io self.original_io = io From 173fd5cf6390650846e8b0647e404742bf11f2a6 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 14:57:47 +0100 Subject: [PATCH 04/19] Skip tests that require lines to be captured --- Lib/test/test_traceback.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 2b5efdd0584aa9..4ead97fd136279 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2293,6 +2293,18 @@ def setUp(self) -> None: sys.modules['io'] = types.SimpleNamespace(StringIO=io.StringIO) return super().setUp() + def test_unhashable(self): + raise unittest.SkipTest( + "test requires source lines to be captured in tracebacks") + + def test_traceback_format_with_cleared_frames(self): + raise unittest.SkipTest( + "test requires source lines to be captured in tracebacks") + + def test_traceback_format(self): + raise unittest.SkipTest( + "test requires source lines to be captured in tracebacks") + def tearDown(self) -> None: sys.modules['io'] = self.original_io traceback._print_exception_bltin = self.original_hook From 6b815615aa136d6df4e70c10306212a729224aa8 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 15:02:05 +0100 Subject: [PATCH 05/19] Add news entry --- .../2025-12-15-15-01-21.gh-issue-142737.xYXzeB.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-15-15-01-21.gh-issue-142737.xYXzeB.rst 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 00000000000000..efa41c9cb9b1f6 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-15-15-01-21.gh-issue-142737.xYXzeB.rst @@ -0,0 +1,2 @@ +Tracebacks will be displayed in fallback mode even if :func:`io.open` is lost. +Patch by Bartosz Sławecki. From 323385ddf9a7e3a22522f28a8961a124f439ea43 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 15:08:52 +0100 Subject: [PATCH 06/19] Goto `error` immediately if we can't get handle to `io.open` --- Objects/call.c | 3 --- Python/traceback.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Objects/call.c b/Objects/call.c index c7a36445985444..41d075caf11ce6 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -729,9 +729,6 @@ _Py_COMP_DIAG_POP PyObject * _PyObject_CallMethodFormat(PyThreadState *tstate, PyObject *callable, const char *format, ...) { - if (callable == NULL) { - return 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 8af63c22a9f84e..264f034dea7fa5 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -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) { From 092356b01f699a710d5dadbddcb494268d9efa43 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 15:21:26 +0100 Subject: [PATCH 07/19] Clarify skip reason --- Lib/test/test_traceback.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 4ead97fd136279..8c4cd54fbcf630 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2295,15 +2295,14 @@ def setUp(self) -> None: def test_unhashable(self): raise unittest.SkipTest( - "test requires source lines to be captured in tracebacks") + "io unavailable (test requires source lines to be captured in tracebacks)") def test_traceback_format_with_cleared_frames(self): raise unittest.SkipTest( - "test requires source lines to be captured in tracebacks") - + "io unavailable (test requires source lines to be captured in tracebacks)") def test_traceback_format(self): raise unittest.SkipTest( - "test requires source lines to be captured in tracebacks") + "io unavailable (test requires source lines to be captured in tracebacks)") def tearDown(self) -> None: sys.modules['io'] = self.original_io From 1a971f44a39d826d7a6253c421f3abe552cc2570 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 15:24:49 +0100 Subject: [PATCH 08/19] Add some docs & comments to tests --- Lib/test/test_traceback.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 8c4cd54fbcf630..1c23f0eb159cd7 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2285,11 +2285,17 @@ def tearDown(self) -> None: @cpython_only @force_not_colorized_test_class class TestFallbackFormatWhenIOUnavailable(unittest.TestCase, TracebackFormatMixin): + """See GH-142737.""" + def setUp(self) -> None: import io self.original_io = io self.original_hook = traceback._print_exception_bltin + + # Triggers fallback path traceback._print_exception_bltin = object() + + # StringIO is still needed for test.support.captured_output() to work sys.modules['io'] = types.SimpleNamespace(StringIO=io.StringIO) return super().setUp() @@ -2300,6 +2306,7 @@ def test_unhashable(self): def test_traceback_format_with_cleared_frames(self): raise unittest.SkipTest( "io unavailable (test requires source lines to be captured in tracebacks)") + def test_traceback_format(self): raise unittest.SkipTest( "io unavailable (test requires source lines to be captured in tracebacks)") From 206633c1b09eb2c2370f391e2482e8fba23421b9 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 15:30:26 +0100 Subject: [PATCH 09/19] Elaborate in the news entry --- .../2025-12-15-15-01-21.gh-issue-142737.xYXzeB.rst | 1 + 1 file changed, 1 insertion(+) 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 index efa41c9cb9b1f6..8b743d1e49de21 100644 --- 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 @@ -1,2 +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. From d95a3d49bd78ef006042518d024cf82d261c0f83 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 15:42:14 +0100 Subject: [PATCH 10/19] Use a subprocess-based test --- Lib/test/test_traceback.py | 57 +++++++++++++++----------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 1c23f0eb159cd7..3e8ea4d28585ef 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -524,6 +524,27 @@ def __del__(self): b'ZeroDivisionError: division by zero'] self.assertEqual(stderr.splitlines(), expected) + def test_when_io_is_lost(self): + # GH-142737: Display the traceback even if io.open is lost + code = textwrap.dedent("""\ + import builtins + import sys + + def bad_import(name, *args, **kwargs): + sys.modules[name] = 42 + return sys.modules[name] + + builtins.__import__ = bad_import + raise RuntimeError("should not crash") + """) + rc, stdout, stderr = assert_python_failure('-c', code) + expected = [b'Exception ignored in the internal traceback machinery:', + b'AttributeError: \'int\' object has no attribute \'_print_exception_bltin\'', + b'Traceback (most recent call last):', + b' File "", line 9, in ', + b'RuntimeError: should not crash'] + self.assertEqual(stderr.splitlines(), expected) + def test_print_exception(self): output = StringIO() traceback.print_exception( @@ -2268,7 +2289,7 @@ class TestTracebackFormat(unittest.TestCase, TracebackFormatMixin): @cpython_only @force_not_colorized_test_class -class TestFallbackFormatWhenPrinterRaises(unittest.TestCase, TracebackFormatMixin): +class TestFallbackTracebackFormat(unittest.TestCase, TracebackFormatMixin): DEBUG_RANGES = False def setUp(self) -> None: self.original_unraisable_hook = sys.unraisablehook @@ -2282,40 +2303,6 @@ def tearDown(self) -> None: sys.unraisablehook = self.original_unraisable_hook return super().tearDown() -@cpython_only -@force_not_colorized_test_class -class TestFallbackFormatWhenIOUnavailable(unittest.TestCase, TracebackFormatMixin): - """See GH-142737.""" - - def setUp(self) -> None: - import io - self.original_io = io - self.original_hook = traceback._print_exception_bltin - - # Triggers fallback path - traceback._print_exception_bltin = object() - - # StringIO is still needed for test.support.captured_output() to work - sys.modules['io'] = types.SimpleNamespace(StringIO=io.StringIO) - return super().setUp() - - def test_unhashable(self): - raise unittest.SkipTest( - "io unavailable (test requires source lines to be captured in tracebacks)") - - def test_traceback_format_with_cleared_frames(self): - raise unittest.SkipTest( - "io unavailable (test requires source lines to be captured in tracebacks)") - - def test_traceback_format(self): - raise unittest.SkipTest( - "io unavailable (test requires source lines to be captured in tracebacks)") - - def tearDown(self) -> None: - sys.modules['io'] = self.original_io - traceback._print_exception_bltin = self.original_hook - return super().tearDown() - class BaseExceptionReportingTests: def get_exception(self, exception_or_callable): From 36a91ad51791282d37539f5d14fa21b13a2af8dc Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 15:46:49 +0100 Subject: [PATCH 11/19] Examine `rc` to make sure it's not a crash --- Lib/test/test_traceback.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 3e8ea4d28585ef..8db79ffb841a26 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -538,6 +538,7 @@ def bad_import(name, *args, **kwargs): raise RuntimeError("should not crash") """) rc, stdout, stderr = assert_python_failure('-c', code) + self.assertEqual(rc, 1) expected = [b'Exception ignored in the internal traceback machinery:', b'AttributeError: \'int\' object has no attribute \'_print_exception_bltin\'', b'Traceback (most recent call last):', From b3d1296593cc497aaafdda9e0683ea780308ba87 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 16:29:20 +0100 Subject: [PATCH 12/19] Simplify minimal reproducer --- Lib/test/test_traceback.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 8db79ffb841a26..ba13b872e9907b 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -527,22 +527,16 @@ def __del__(self): def test_when_io_is_lost(self): # GH-142737: Display the traceback even if io.open is lost code = textwrap.dedent("""\ - import builtins - import sys - - def bad_import(name, *args, **kwargs): - sys.modules[name] = 42 - return sys.modules[name] - - builtins.__import__ = bad_import + import io + import traceback + traceback._print_exception_bltin = None + del io.open raise RuntimeError("should not crash") """) rc, stdout, stderr = assert_python_failure('-c', code) self.assertEqual(rc, 1) - expected = [b'Exception ignored in the internal traceback machinery:', - b'AttributeError: \'int\' object has no attribute \'_print_exception_bltin\'', - b'Traceback (most recent call last):', - b' File "", line 9, in ', + expected = [b'Traceback (most recent call last):', + b' File "", line 5, in ', b'RuntimeError: should not crash'] self.assertEqual(stderr.splitlines(), expected) From e0933bb95c4318cd68690726554a897766b4b761 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 16:45:05 +0100 Subject: [PATCH 13/19] Fiat lux --- Lib/test/test_traceback.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index ba13b872e9907b..dd3d9d49226791 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -18,6 +18,8 @@ from test.support import (Error, captured_output, cpython_only, ALWAYS_EQ, requires_debug_ranges, has_no_debug_ranges, requires_subprocess) +from test.support import script_helper +from test.support import os_helper from test.support.os_helper import TESTFN, unlink from test.support.script_helper import assert_python_ok, assert_python_failure, make_script from test.support.import_helper import forget @@ -524,7 +526,7 @@ def __del__(self): b'ZeroDivisionError: division by zero'] self.assertEqual(stderr.splitlines(), expected) - def test_when_io_is_lost(self): + def test_lost_io_open(self): # GH-142737: Display the traceback even if io.open is lost code = textwrap.dedent("""\ import io @@ -533,10 +535,13 @@ def test_when_io_is_lost(self): del io.open raise RuntimeError("should not crash") """) - rc, stdout, stderr = assert_python_failure('-c', code) - self.assertEqual(rc, 1) + with os_helper.temp_dir() as temp_dir: + script = script_helper.make_script( + script_dir=temp_dir, script_basename='tb_test_no_io_open', source=code) + 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):', - b' File "", line 5, in ', + f' File "{script}", line 5, in '.encode(), b'RuntimeError: should not crash'] self.assertEqual(stderr.splitlines(), expected) From 6589e9f40a1eedf8a583996f3f88f0e9e90cb04b Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 16:46:40 +0100 Subject: [PATCH 14/19] Fix formatting --- Lib/test/test_traceback.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index dd3d9d49226791..3be1b6e7d0cfb8 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -18,9 +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 import script_helper -from test.support import os_helper -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 @@ -528,18 +526,23 @@ def __del__(self): def test_lost_io_open(self): # GH-142737: Display the traceback even if io.open is lost - code = textwrap.dedent("""\ + crasher = textwrap.dedent("""\ import io import traceback traceback._print_exception_bltin = None del io.open raise RuntimeError("should not crash") """) - with os_helper.temp_dir() as temp_dir: - script = script_helper.make_script( - script_dir=temp_dir, script_basename='tb_test_no_io_open', source=code) + + 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 5, in '.encode(), b'RuntimeError: should not crash'] From bea29ea7031ef562dfdde90c293b7c75d2bc2445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Mon, 15 Dec 2025 16:50:26 +0100 Subject: [PATCH 15/19] Apply suggestion from @johnslavik --- Lib/test/test_traceback.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 3be1b6e7d0cfb8..66019cb5b736ab 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -529,6 +529,7 @@ def test_lost_io_open(self): crasher = textwrap.dedent("""\ import io import traceback + # Trigger fallback mode traceback._print_exception_bltin = None del io.open raise RuntimeError("should not crash") From 77effbc55ea7eb89b3b30a4e64cdce6daf770c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Mon, 15 Dec 2025 17:09:52 +0100 Subject: [PATCH 16/19] Apply suggestion from @johnslavik --- Lib/test/test_traceback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 66019cb5b736ab..a81752d0a989b5 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -545,7 +545,7 @@ def test_lost_io_open(self): self.assertEqual(rc, 1) # Make sure it's not a crash expected = [b'Traceback (most recent call last):', - f' File "{script}", line 5, in '.encode(), + f' File "{script}", line 6, in '.encode(), b'RuntimeError: should not crash'] self.assertEqual(stderr.splitlines(), expected) From 45774d205efa1f8f8e15ce6bb83ce12166d003e3 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 21:00:50 +0100 Subject: [PATCH 17/19] Add `assert(callable != NULL)` in `_PyObject_CallMethodFormat` --- Objects/call.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/call.c b/Objects/call.c index 41d075caf11ce6..af42fc8f7f2dbf 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -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); From ae292f554c1225353efdb0f52188ab82a18e3b18 Mon Sep 17 00:00:00 2001 From: johnslavik Date: Mon, 15 Dec 2025 23:24:54 +0100 Subject: [PATCH 18/19] Mark as CPython-only and mention `_Py_FindSourceFile` --- Lib/test/test_traceback.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index a81752d0a989b5..790f2c814e5672 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -524,6 +524,7 @@ 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("""\ @@ -535,6 +536,7 @@ def test_lost_io_open(self): raise RuntimeError("should not crash") """) + # Create a temporary script to go through _Py_FindSourceFile with temp_dir() as script_dir: script = make_script( script_dir=script_dir, From f9aae298a25084ed653cb033b79d9af87ee86691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20S=C5=82awecki?= Date: Mon, 15 Dec 2025 23:30:15 +0100 Subject: [PATCH 19/19] Better wording in the comment --- Lib/test/test_traceback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 790f2c814e5672..96510eeec54640 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -536,7 +536,7 @@ def test_lost_io_open(self): raise RuntimeError("should not crash") """) - # Create a temporary script to go through _Py_FindSourceFile + # Create a temporary script to exercise _Py_FindSourceFile with temp_dir() as script_dir: script = make_script( script_dir=script_dir,