From ad8d57482c8ca652a93f4511f0fb4a0ac89a80ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 1 Mar 2025 12:37:20 +0100 Subject: [PATCH 1/3] fix REPL traceback report --- Lib/_pyrepl/__main__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/_pyrepl/__main__.py b/Lib/_pyrepl/__main__.py index 3fa992eee8eeff..9c66812e13afa6 100644 --- a/Lib/_pyrepl/__main__.py +++ b/Lib/_pyrepl/__main__.py @@ -1,6 +1,10 @@ # Important: don't add things to this module, as they will end up in the REPL's # default globals. Use _pyrepl.main instead. +# Avoid caching this file by linecache and incorrectly report tracebacks. +# See https://github.com/python/cpython/issues/129098. +__spec__ = __loader__ = None + if __name__ == "__main__": from .main import interactive_console as __pyrepl_interactive_console __pyrepl_interactive_console() From 8eb3cca8f76335f6c98732e7290d3932fe082927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sat, 1 Mar 2025 12:37:12 +0100 Subject: [PATCH 2/3] blurb --- .../next/Library/2025-03-01-12-37-08.gh-issue-129098.eJ2-6L.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-03-01-12-37-08.gh-issue-129098.eJ2-6L.rst diff --git a/Misc/NEWS.d/next/Library/2025-03-01-12-37-08.gh-issue-129098.eJ2-6L.rst b/Misc/NEWS.d/next/Library/2025-03-01-12-37-08.gh-issue-129098.eJ2-6L.rst new file mode 100644 index 00000000000000..8ac9082a7b4d94 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-01-12-37-08.gh-issue-129098.eJ2-6L.rst @@ -0,0 +1,2 @@ +Fix REPL traceback reporting when using :func:`compile` with an inexisting +file. Patch by Bénédikt Tran. From 9b0565da5966ac829b5d556b40477ad268eb7afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dikt=20Tran?= <10796600+picnixz@users.noreply.github.com> Date: Sun, 2 Mar 2025 12:07:46 +0100 Subject: [PATCH 3/3] add tests --- Lib/test/test_pyrepl/test_pyrepl.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_pyrepl/test_pyrepl.py b/Lib/test/test_pyrepl/test_pyrepl.py index 3540d2a5a41662..3910513309b403 100644 --- a/Lib/test/test_pyrepl/test_pyrepl.py +++ b/Lib/test/test_pyrepl/test_pyrepl.py @@ -11,9 +11,9 @@ from unittest import TestCase, skipUnless, skipIf from unittest.mock import patch from test.support import force_not_colorized, make_clean_env -from test.support import SHORT_TIMEOUT +from test.support import SHORT_TIMEOUT, STDLIB_DIR from test.support.import_helper import import_module -from test.support.os_helper import unlink +from test.support.os_helper import EnvironmentVarGuard, unlink from .support import ( FakeConsole, @@ -1216,6 +1216,31 @@ def test_python_basic_repl(self): self.assertNotIn("Exception", output) self.assertNotIn("Traceback", output) + @force_not_colorized + def test_no_pyrepl_source_in_exc(self): + # Avoid using _pyrepl/__main__.py in traceback reports + # See https://github.com/python/cpython/issues/129098. + pyrepl_main_file = os.path.join(STDLIB_DIR, "_pyrepl", "__main__.py") + self.assertTrue(os.path.exists(pyrepl_main_file), pyrepl_main_file) + with open(pyrepl_main_file) as fp: + excluded_lines = fp.readlines() + excluded_lines = list(filter(None, map(str.strip, excluded_lines))) + + for filename in ['?', 'unknown-filename', '', '<...>']: + self._test_no_pyrepl_source_in_exc(filename, excluded_lines) + + def _test_no_pyrepl_source_in_exc(self, filename, excluded_lines): + with EnvironmentVarGuard() as env, self.subTest(filename=filename): + env.unset("PYTHON_BASIC_REPL") + commands = (f"eval(compile('spam', {filename!r}, 'eval'))\n" + f"exit()\n") + output, _ = self.run_repl(commands, env=env) + self.assertIn("Traceback (most recent call last)", output) + self.assertIn("NameError: name 'spam' is not defined", output) + for line in excluded_lines: + with self.subTest(line=line): + self.assertNotIn(line, output) + @force_not_colorized def test_bad_sys_excepthook_doesnt_crash_pyrepl(self): env = os.environ.copy()