Skip to content

Commit

Permalink
pythongh-111201: Allow pasted code to contain multiple statements in …
Browse files Browse the repository at this point in the history
…the REPL (python#118712)

Co-authored-by: Łukasz Langa <[email protected]>
  • Loading branch information
pablogsal and ambv authored May 7, 2024
1 parent 26bab42 commit a94ac56
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 9 deletions.
5 changes: 4 additions & 1 deletion Lib/_pyrepl/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,15 +460,18 @@ def do(self) -> None:
class paste_mode(Command):

def do(self) -> None:
if not self.reader.paste_mode:
self.reader.was_paste_mode_activated = True
self.reader.paste_mode = not self.reader.paste_mode
self.reader.dirty = True


class enable_bracketed_paste(Command):
def do(self) -> None:
self.reader.paste_mode = True
self.reader.was_paste_mode_activated = True

class disable_bracketed_paste(Command):
def do(self) -> None:
self.reader.paste_mode = False
self.reader.insert("\n")
self.reader.dirty = True
1 change: 1 addition & 0 deletions Lib/_pyrepl/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ class Reader:
dirty: bool = False
finished: bool = False
paste_mode: bool = False
was_paste_mode_activated: bool = False
commands: dict[str, type[Command]] = field(default_factory=make_default_commands)
last_command: type[Command] | None = None
syntax_table: dict[str, int] = field(default_factory=make_default_syntax_table)
Expand Down
3 changes: 2 additions & 1 deletion Lib/_pyrepl/readline.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,11 @@ def multiline_input(self, more_lines, ps1, ps2):
reader.more_lines = more_lines
reader.ps1 = reader.ps2 = ps1
reader.ps3 = reader.ps4 = ps2
return reader.readline()
return reader.readline(), reader.was_paste_mode_activated
finally:
reader.more_lines = saved
reader.paste_mode = False
reader.was_paste_mode_activated = False

def parse_and_bind(self, string: str) -> None:
pass # XXX we don't support parsing GNU-readline-style init files
Expand Down
7 changes: 5 additions & 2 deletions Lib/_pyrepl/simple_interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def more_lines(unicodetext: str) -> bool:
ps1 = getattr(sys, "ps1", ">>> ")
ps2 = getattr(sys, "ps2", "... ")
try:
statement = multiline_input(more_lines, ps1, ps2)
statement, contains_pasted_code = multiline_input(more_lines, ps1, ps2)
except EOFError:
break

Expand All @@ -144,7 +144,10 @@ def more_lines(unicodetext: str) -> bool:

input_name = f"<python-input-{input_n}>"
linecache._register_code(input_name, statement, "<stdin>") # type: ignore[attr-defined]
more = console.push(_strip_final_indent(statement), filename=input_name) # type: ignore[call-arg]
symbol = "single" if not contains_pasted_code else "exec"
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol=symbol) # type: ignore[call-arg]
if contains_pasted_code and more:
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol="single") # type: ignore[call-arg]
assert not more
input_n += 1
except KeyboardInterrupt:
Expand Down
4 changes: 2 additions & 2 deletions Lib/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ def interact(self, banner=None, exitmsg=None):
elif exitmsg != '':
self.write('%s\n' % exitmsg)

def push(self, line, filename=None):
def push(self, line, filename=None, _symbol="single"):
"""Push a line to the interpreter.
The line should not have a trailing newline; it may have
Expand All @@ -299,7 +299,7 @@ def push(self, line, filename=None):
source = "\n".join(self.buffer)
if filename is None:
filename = self.filename
more = self.runsource(source, filename)
more = self.runsource(source, filename, symbol=_symbol)
if not more:
self.resetbuffer()
return more
Expand Down
22 changes: 19 additions & 3 deletions Lib/test/test_pyrepl.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from _pyrepl.readline import ReadlineAlikeReader, ReadlineConfig
from _pyrepl.simple_interact import _strip_final_indent
from _pyrepl.unix_eventqueue import EventQueue
from _pyrepl.simple_interact import InteractiveColoredConsole


def more_lines(unicodetext, namespace=None):
Expand Down Expand Up @@ -830,7 +831,6 @@ def test_bracketed_paste(self):
' else:\n'
' pass\n'
)
# fmt: on

output_code = (
'def a():\n'
Expand All @@ -841,8 +841,8 @@ def test_bracketed_paste(self):
'\n'
' else:\n'
' pass\n'
'\n'
)
# fmt: on

paste_start = "\x1b[200~"
paste_end = "\x1b[201~"
Expand All @@ -857,6 +857,22 @@ def test_bracketed_paste(self):
output = multiline_input(reader)
self.assertEqual(output, output_code)

def test_bracketed_paste_single_line(self):
input_code = "oneline"

paste_start = "\x1b[200~"
paste_end = "\x1b[201~"

events = itertools.chain(
code_to_events(paste_start),
code_to_events(input_code),
code_to_events(paste_end),
code_to_events("\n"),
)
reader = self.prepare_reader(events)
output = multiline_input(reader)
self.assertEqual(output, input_code)


class TestReader(TestCase):
def assert_screen_equals(self, reader, expected):
Expand Down Expand Up @@ -986,5 +1002,5 @@ def test_up_arrow_after_ctrl_r(self):
self.assert_screen_equals(reader, "")


if __name__ == "__main__":
if __name__ == '__main__':
unittest.main()

0 comments on commit a94ac56

Please sign in to comment.