]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-128231: Use `runcode()` return value for failing early (GH-129488) (#130513)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 28 Mar 2025 11:11:45 +0000 (12:11 +0100)
committerGitHub <noreply@github.com>
Fri, 28 Mar 2025 11:11:45 +0000 (13:11 +0200)
gh-128231: Use `runcode()` return value for failing early (GH-129488)
(cherry picked from commit 7ed3dc6392613832f66c63507385b1da109cbf21)

Co-authored-by: Bartosz Sławecki <bartoszpiotrslawecki@gmail.com>
Lib/_pyrepl/console.py
Lib/asyncio/__main__.py
Lib/test/test_pyrepl/test_interact.py
Lib/test/test_repl.py
Misc/NEWS.d/next/Library/2025-01-30-22-49-42.gh-issue-128231.SuEC18.rst [new file with mode: 0644]

index 3c1ca6f682659da7855245b672d7c6c9c2fdf48f..8956fb1242e52a46c02e5dcf53315427fd59cddf 100644 (file)
@@ -153,6 +153,8 @@ class Console(ABC):
 
 
 class InteractiveColoredConsole(code.InteractiveConsole):
+    STATEMENT_FAILED = object()
+
     def __init__(
         self,
         locals: dict[str, object] | None = None,
@@ -174,6 +176,16 @@ class InteractiveColoredConsole(code.InteractiveConsole):
                 limit=traceback.BUILTIN_EXCEPTION_LIMIT)
         self.write(''.join(lines))
 
+    def runcode(self, code):
+        try:
+            exec(code, self.locals)
+        except SystemExit:
+            raise
+        except BaseException:
+            self.showtraceback()
+            return self.STATEMENT_FAILED
+        return None
+
     def runsource(self, source, filename="<input>", symbol="single"):
         try:
             tree = self.compile.compiler(
@@ -211,5 +223,7 @@ class InteractiveColoredConsole(code.InteractiveConsole):
             if code is None:
                 return True
 
-            self.runcode(code)
+            result = self.runcode(code)
+            if result is self.STATEMENT_FAILED:
+                break
         return False
index 95c636f9e028665523b639d01e9151c0af7cc5f7..69f5a30cfe509591f3c1878ec6ae2807c8d569a9 100644 (file)
@@ -75,7 +75,7 @@ class AsyncIOInteractiveConsole(InteractiveColoredConsole):
                 self.write("\nKeyboardInterrupt\n")
             else:
                 self.showtraceback()
-
+            return self.STATEMENT_FAILED
 
 class REPLThread(threading.Thread):
 
index 8b941b93670e843370b58569f98e6f73c84d39c4..2651cf7d32d79da2e1ee3f11b2d937ce55db815e 100644 (file)
@@ -53,6 +53,19 @@ class TestSimpleInteract(unittest.TestCase):
         self.assertFalse(more)
         self.assertEqual(f.getvalue(), "1\n")
 
+    @force_not_colorized
+    def test_multiple_statements_fail_early(self):
+        console = InteractiveColoredConsole()
+        code = dedent("""\
+        raise Exception('foobar')
+        print('spam&eggs')
+        """)
+        f = io.StringIO()
+        with contextlib.redirect_stderr(f):
+            console.runsource(code)
+        self.assertIn('Exception: foobar', f.getvalue())
+        self.assertNotIn('spam&eggs', f.getvalue())
+
     def test_empty(self):
         namespace = {}
         code = ""
index fdaff5b99bb803115554b873bef36b0fbe6263c9..1e4b48622b93838fc5c29d1cb344aa674f5eb1d6 100644 (file)
@@ -294,7 +294,15 @@ class TestInteractiveModeSyntaxErrors(unittest.TestCase):
         self.assertEqual(traceback_lines, expected_lines)
 
 
-class TestAsyncioREPLContextVars(unittest.TestCase):
+class TestAsyncioREPL(unittest.TestCase):
+    def test_multiple_statements_fail_early(self):
+        user_input = "1 / 0; print('afterwards')"
+        p = spawn_repl("-m", "asyncio")
+        p.stdin.write(user_input)
+        output = kill_python(p)
+        self.assertIn("ZeroDivisionError", output)
+        self.assertNotIn("afterwards", output)
+
     def test_toplevel_contextvars_sync(self):
         user_input = dedent("""\
         from contextvars import ContextVar
diff --git a/Misc/NEWS.d/next/Library/2025-01-30-22-49-42.gh-issue-128231.SuEC18.rst b/Misc/NEWS.d/next/Library/2025-01-30-22-49-42.gh-issue-128231.SuEC18.rst
new file mode 100644 (file)
index 0000000..a70b6a1
--- /dev/null
@@ -0,0 +1,2 @@
+Execution of multiple statements in the new REPL now stops immediately upon
+the first exception encountered. Patch by Bartosz SÅ‚awecki.