Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit fcc44e6

Browse filesBrowse files
committed
gh-118893: Evaluate all statements in the new REPL separately
1 parent c4f9823 commit fcc44e6
Copy full SHA for fcc44e6

File tree

Expand file treeCollapse file tree

2 files changed

+82
-5
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+82
-5
lines changed

‎Lib/_pyrepl/simple_interact.py

Copy file name to clipboardExpand all lines: Lib/_pyrepl/simple_interact.py
+22-4Lines changed: 22 additions & 4 deletions
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,26 @@ 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+
if tree.body:
84+
*_, last_stmt = tree.body
85+
for stmt in tree.body:
86+
wrapper = ast.Interactive if stmt is last_stmt else ast.Module
87+
the_symbol = symbol if stmt is last_stmt else "exec"
88+
item = wrapper([stmt])
89+
try:
90+
code = compile(item, filename, the_symbol)
91+
except (OverflowError, ValueError):
92+
self.showsyntaxerror(filename)
93+
return False
94+
95+
if code is None:
96+
return True
97+
98+
self.runcode(code)
99+
return False
100+
80101

81102
def run_multiline_interactive_console(
82103
mainmodule: ModuleType | None= None, future_flags: int = 0
@@ -144,10 +165,7 @@ def more_lines(unicodetext: str) -> bool:
144165

145166
input_name = f"<python-input-{input_n}>"
146167
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]
168+
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol="single") # type: ignore[call-arg]
151169
assert not more
152170
input_n += 1
153171
except KeyboardInterrupt:

‎Lib/test/test_pyrepl.py

Copy file name to clipboardExpand all lines: Lib/test/test_pyrepl.py
+60-1Lines changed: 60 additions & 1 deletion
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,62 @@ 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+
def test_empty(self):
1050+
namespace = {}
1051+
code = ""
1052+
console = InteractiveColoredConsole(namespace, filename="<stdin>")
1053+
f = io.StringIO()
1054+
with contextlib.redirect_stdout(f):
1055+
more = console.push(code, filename="<stdin>", _symbol="single") # type: ignore[call-arg]
1056+
self.assertFalse(more)
1057+
self.assertEqual(f.getvalue(), "")
1058+
1059+
1060+
if __name__ == '__main__':
1061+
unittest.main()
1062+
1063+
10051064
if __name__ == '__main__':
10061065
unittest.main()

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.