diff --git a/Lib/_pyrepl/simple_interact.py b/Lib/_pyrepl/simple_interact.py index c169d0191bd8333..6de6b9995f1b2ae 100644 --- a/Lib/_pyrepl/simple_interact.py +++ b/Lib/_pyrepl/simple_interact.py @@ -83,7 +83,7 @@ def _more_lines(console: code.InteractiveConsole, unicodetext: str) -> bool: src = _strip_final_indent(unicodetext) try: code = console.compile(src, "", "single") - except (OverflowError, SyntaxError, ValueError): + except (OverflowError, SyntaxError, ValueError, SystemError): lines = src.splitlines(keepends=True) if len(lines) == 1: return False diff --git a/Lib/code.py b/Lib/code.py index df1d7199e339341..887072749a12715 100644 --- a/Lib/code.py +++ b/Lib/code.py @@ -44,8 +44,8 @@ def runsource(self, source, filename="", symbol="single"): One of several things can happen: 1) The input is incorrect; compile_command() raised an - exception (SyntaxError or OverflowError). A syntax traceback - will be printed by calling the showsyntaxerror() method. + exception. A syntax traceback will be printed by calling + the showsyntaxerror() method. 2) The input is incomplete, and more input is required; compile_command() returned None. Nothing happens. @@ -62,7 +62,7 @@ def runsource(self, source, filename="", symbol="single"): """ try: code = self.compile(source, filename, symbol) - except (OverflowError, SyntaxError, ValueError): + except (OverflowError, SyntaxError, ValueError, SystemError): # Case 1 self.showsyntaxerror(filename, source=source) return False diff --git a/Lib/codeop.py b/Lib/codeop.py index 40e88423119bc4f..f4b5e9717386654 100644 --- a/Lib/codeop.py +++ b/Lib/codeop.py @@ -66,7 +66,7 @@ def _maybe_compile(compiler, source, filename, symbol, flags): try: compiler(source + "\n", filename, symbol, flags=flags) return None - except _IncompleteInputError: + except _IncompleteInputError, SystemError: return None except SyntaxError: pass @@ -147,8 +147,8 @@ def __call__(self, source, filename="", symbol="single"): - Return a code object if the command is complete and valid - Return None if the command is incomplete - - Raise SyntaxError, ValueError or OverflowError if the command is a - syntax error (OverflowError and ValueError can be produced by - malformed literals). + - Raise SyntaxError, ValueError, OverflowError or SystemError. + OverflowError and ValueError can be produced by malformed + literals, SystemError if source cannot be compiled. """ return _maybe_compile(self.compiler, source, filename, symbol, flags=self.compiler.flags) diff --git a/Lib/test/test_code_module.py b/Lib/test/test_code_module.py index 3642b47c2c1f03f..6143baf59340ed0 100644 --- a/Lib/test/test_code_module.py +++ b/Lib/test/test_code_module.py @@ -324,6 +324,15 @@ def test_context_tb(self): self.assertIsNotNone(self.sysmod.last_traceback) self.assertIs(self.sysmod.last_exc, self.sysmod.last_value) + def test_system_error(self): + # SystemError from the compiler should be reported (runsource returns + # False) rather than propagating to the caller. + from unittest import mock + err = SystemError("compiler internal error") + self.console.compile = mock.Mock(side_effect=err) + result = self.console.runsource("x = 1") + self.assertFalse(result) + class TestInteractiveConsoleLocalExit(unittest.TestCase, MockSys): diff --git a/Lib/test/test_pyrepl/support.py b/Lib/test/test_pyrepl/support.py index c879a2f93b63134..ebccf9822bb899c 100644 --- a/Lib/test/test_pyrepl/support.py +++ b/Lib/test/test_pyrepl/support.py @@ -44,7 +44,7 @@ def more_lines(text: str, namespace: dict | None = None): console = InteractiveConsole(namespace, filename="") try: code = console.compile(src, "", "single") - except (OverflowError, SyntaxError, ValueError): + except (OverflowError, SyntaxError, ValueError, SystemError): return False else: return code is None diff --git a/Lib/test/test_pyrepl/test_interact.py b/Lib/test/test_pyrepl/test_interact.py index fd4530ebc004aa7..55dadb9a6e3aaf9 100644 --- a/Lib/test/test_pyrepl/test_interact.py +++ b/Lib/test/test_pyrepl/test_interact.py @@ -275,6 +275,13 @@ def test_incomplete_statement(self): console = InteractiveColoredConsole(namespace, filename="") self.assertTrue(_more_lines(console, code)) + def test_system_error_during_compilation(self): + namespace = {} + console = InteractiveColoredConsole(namespace, filename="") + with patch.object(console, "compile", side_effect=SystemError("compiler bug")): + result = _more_lines(console, "some code") + self.assertFalse(result) + class TestWarnings(unittest.TestCase): def test_pep_765_warning(self): diff --git a/Misc/NEWS.d/next/Library/2026-06-01-21-31-06.gh-issue-150732.WqHBvP.rst b/Misc/NEWS.d/next/Library/2026-06-01-21-31-06.gh-issue-150732.WqHBvP.rst new file mode 100644 index 000000000000000..e9423988e58d5f1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-01-21-31-06.gh-issue-150732.WqHBvP.rst @@ -0,0 +1 @@ +Handle :exc:`SystemError` from the compiler in ``PyREPL``, :mod:`code`, and :mod:`codeop`. Patch by Bartosz Sławecki.