Skip to content

Commit 4315668

Browse files
committed
gh-111201: Allow bracketed paste to work
1 parent afbe5bf commit 4315668

File tree

4 files changed

+65
-0
lines changed

4 files changed

+65
-0
lines changed

Lib/_pyrepl/commands.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,3 +462,13 @@ class paste_mode(Command):
462462
def do(self) -> None:
463463
self.reader.paste_mode = not self.reader.paste_mode
464464
self.reader.dirty = True
465+
466+
467+
class enable_bracketed_paste(Command):
468+
def do(self) -> None:
469+
self.reader.paste_mode = True
470+
471+
class disable_bracketed_paste(Command):
472+
def do(self) -> None:
473+
self.reader.paste_mode = False
474+
self.reader.insert("\n")

Lib/_pyrepl/reader.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ def make_default_commands() -> dict[CommandName, type[Command]]:
128128
(r"\M-9", "digit-arg"),
129129
# (r'\M-\n', 'insert-nl'),
130130
("\\\\", "self-insert"),
131+
(r"\x1b[200~", "enable_bracketed_paste"),
132+
(r"\x1b[201~", "disable_bracketed_paste"),
131133
]
132134
+ [(c, "self-insert") for c in map(chr, range(32, 127)) if c != "\\"]
133135
+ [(c, "self-insert") for c in map(chr, range(128, 256)) if c.isalpha()]

Lib/_pyrepl/simple_interact.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ def showtraceback(self):
7979
super().showtraceback(colorize=self.can_colorize)
8080

8181

82+
def _enable_bracketed_paste() -> None:
83+
sys.stdout.write("\x1b[?2004h")
84+
sys.stdout.flush()
85+
86+
87+
def _disable_bracketed_paste() -> None:
88+
sys.stdout.write("\x1b[?2004l")
89+
sys.stdout.flush()
90+
91+
8292
def run_multiline_interactive_console(
8393
mainmodule: ModuleType | None= None, future_flags: int = 0
8494
) -> None:
@@ -137,9 +147,12 @@ def more_lines(unicodetext: str) -> bool:
137147
ps1 = getattr(sys, "ps1", ">>> ")
138148
ps2 = getattr(sys, "ps2", "... ")
139149
try:
150+
_enable_bracketed_paste()
140151
statement = multiline_input(more_lines, ps1, ps2)
141152
except EOFError:
142153
break
154+
finally:
155+
_disable_bracketed_paste()
143156

144157
if maybe_run_command(statement):
145158
continue

Lib/test/test_pyrepl.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,46 @@ def test_paste_not_in_paste_mode(self):
811811
reader = self.prepare_reader(events)
812812
output = multiline_input(reader)
813813
self.assertEqual(output, output_code)
814+
815+
def test_bracketed_paste(self):
816+
"""Test that bracketed paste using \x1b[200~ and \x1b[201~ works."""
817+
# fmt: off
818+
input_code = (
819+
'def a():\n'
820+
' for x in range(10):\n'
821+
'\n'
822+
' if x%2:\n'
823+
' print(x)\n'
824+
'\n'
825+
' else:\n'
826+
' pass\n'
827+
)
828+
# fmt: on
829+
830+
output_code = (
831+
'def a():\n'
832+
' for x in range(10):\n'
833+
'\n'
834+
' if x%2:\n'
835+
' print(x)\n'
836+
'\n'
837+
' else:\n'
838+
' pass\n'
839+
'\n'
840+
)
841+
842+
paste_start = "\x1b[200~"
843+
paste_end = "\x1b[201~"
844+
845+
events = itertools.chain(
846+
code_to_events(paste_start),
847+
code_to_events(input_code),
848+
code_to_events(paste_end),
849+
code_to_events("\n"),
850+
)
851+
reader = self.prepare_reader(events)
852+
output = multiline_input(reader)
853+
self.assertEqual(output, output_code)
814854

815855

816856
class TestReader(TestCase):

0 commit comments

Comments
 (0)