Skip to content

Commit c40ed58

Browse files
committed
pythongh-118893: Evaluate all statements in the new REPL separately
1 parent c4f9823 commit c40ed58

File tree

2 files changed

+72
-5
lines changed

2 files changed

+72
-5
lines changed

Lib/_pyrepl/simple_interact.py

+21-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import linecache
3131
import sys
3232
import code
33+
import ast
3334
from types import ModuleType
3435

3536
from .readline import _get_reader, multiline_input
@@ -77,6 +78,25 @@ def __init__(
7778
def showtraceback(self):
7879
super().showtraceback(colorize=self.can_colorize)
7980

81+
def runsource(self, source, filename="<input>", symbol="single"):
82+
tree = ast.parse(source)
83+
*_, last_stmt = tree.body
84+
for stmt in tree.body:
85+
wrapper = ast.Interactive if stmt is last_stmt else ast.Module
86+
the_symbol = symbol if stmt is last_stmt else "exec"
87+
item = wrapper([stmt])
88+
try:
89+
code = compile(item, filename, the_symbol)
90+
except (OverflowError, ValueError):
91+
self.showsyntaxerror(filename)
92+
return False
93+
94+
if code is None:
95+
return True
96+
97+
self.runcode(code)
98+
return False
99+
80100

81101
def run_multiline_interactive_console(
82102
mainmodule: ModuleType | None= None, future_flags: int = 0
@@ -144,10 +164,7 @@ def more_lines(unicodetext: str) -> bool:
144164

145165
input_name = f"<python-input-{input_n}>"
146166
linecache._register_code(input_name, statement, "<stdin>") # type: ignore[attr-defined]
147-
symbol = "single" if not contains_pasted_code else "exec"
148-
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol=symbol) # type: ignore[call-arg]
149-
if contains_pasted_code and more:
150-
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol="single") # type: ignore[call-arg]
167+
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol="single") # type: ignore[call-arg]
151168
assert not more
152169
input_n += 1
153170
except KeyboardInterrupt:

Lib/test/test_pyrepl.py

+51-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import itertools
22
import os
33
import rlcompleter
4-
import sys
54
import tempfile
65
import unittest
76
from code import InteractiveConsole
87
from functools import partial
98
from unittest import TestCase
109
from unittest.mock import MagicMock, patch
10+
from textwrap import dedent
11+
import contextlib
12+
import io
1113

1214
from test.support import requires
1315
from test.support.import_helper import import_module
@@ -1002,5 +1004,53 @@ def test_up_arrow_after_ctrl_r(self):
10021004
self.assert_screen_equals(reader, "")
10031005

10041006

1007+
class TestSimpleInteract(unittest.TestCase):
1008+
def test_multiple_statements(self):
1009+
namespace = {}
1010+
code = dedent("""\
1011+
class A:
1012+
def foo(self):
1013+
1014+
1015+
pass
1016+
1017+
class B:
1018+
def bar(self):
1019+
pass
1020+
1021+
a = 1
1022+
a
1023+
""")
1024+
console = InteractiveColoredConsole(namespace, filename="<stdin>")
1025+
with (
1026+
patch.object(InteractiveColoredConsole, "showsyntaxerror") as showsyntaxerror,
1027+
patch.object(InteractiveColoredConsole, "runsource", wraps=console.runsource) as runsource,
1028+
):
1029+
more = console.push(code, filename="<stdin>", _symbol="single") # type: ignore[call-arg]
1030+
self.assertFalse(more)
1031+
showsyntaxerror.assert_not_called()
1032+
1033+
1034+
def test_multiple_statements_output(self):
1035+
namespace = {}
1036+
code = dedent("""\
1037+
b = 1
1038+
b
1039+
a = 1
1040+
a
1041+
""")
1042+
console = InteractiveColoredConsole(namespace, filename="<stdin>")
1043+
f = io.StringIO()
1044+
with contextlib.redirect_stdout(f):
1045+
more = console.push(code, filename="<stdin>", _symbol="single") # type: ignore[call-arg]
1046+
self.assertFalse(more)
1047+
self.assertEqual(f.getvalue(), "1\n")
1048+
1049+
1050+
1051+
if __name__ == '__main__':
1052+
unittest.main()
1053+
1054+
10051055
if __name__ == '__main__':
10061056
unittest.main()

0 commit comments

Comments
 (0)