From 431566850982f69df5bbc6419e21f8086677cad4 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 7 May 2024 12:49:52 +0100 Subject: [PATCH 1/6] gh-111201: Allow bracketed paste to work --- Lib/_pyrepl/commands.py | 10 +++++++++ Lib/_pyrepl/reader.py | 2 ++ Lib/_pyrepl/simple_interact.py | 13 +++++++++++ Lib/test/test_pyrepl.py | 40 ++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+) diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index 60ceb30d2cd77d..9c11cf87abcf16 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -462,3 +462,13 @@ class paste_mode(Command): def do(self) -> None: 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 + +class disable_bracketed_paste(Command): + def do(self) -> None: + self.reader.paste_mode = False + self.reader.insert("\n") \ No newline at end of file diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py index a7ef988da12a6a..1c80196e884cae 100644 --- a/Lib/_pyrepl/reader.py +++ b/Lib/_pyrepl/reader.py @@ -128,6 +128,8 @@ def make_default_commands() -> dict[CommandName, type[Command]]: (r"\M-9", "digit-arg"), # (r'\M-\n', 'insert-nl'), ("\\\\", "self-insert"), + (r"\x1b[200~", "enable_bracketed_paste"), + (r"\x1b[201~", "disable_bracketed_paste"), ] + [(c, "self-insert") for c in map(chr, range(32, 127)) if c != "\\"] + [(c, "self-insert") for c in map(chr, range(128, 256)) if c.isalpha()] diff --git a/Lib/_pyrepl/simple_interact.py b/Lib/_pyrepl/simple_interact.py index ce79d0d547ebce..98dcbba7f300fb 100644 --- a/Lib/_pyrepl/simple_interact.py +++ b/Lib/_pyrepl/simple_interact.py @@ -79,6 +79,16 @@ def showtraceback(self): super().showtraceback(colorize=self.can_colorize) +def _enable_bracketed_paste() -> None: + sys.stdout.write("\x1b[?2004h") + sys.stdout.flush() + + +def _disable_bracketed_paste() -> None: + sys.stdout.write("\x1b[?2004l") + sys.stdout.flush() + + def run_multiline_interactive_console( mainmodule: ModuleType | None= None, future_flags: int = 0 ) -> None: @@ -137,9 +147,12 @@ def more_lines(unicodetext: str) -> bool: ps1 = getattr(sys, "ps1", ">>> ") ps2 = getattr(sys, "ps2", "... ") try: + _enable_bracketed_paste() statement = multiline_input(more_lines, ps1, ps2) except EOFError: break + finally: + _disable_bracketed_paste() if maybe_run_command(statement): continue diff --git a/Lib/test/test_pyrepl.py b/Lib/test/test_pyrepl.py index 3cba37c70e9557..82e2a2569a270a 100644 --- a/Lib/test/test_pyrepl.py +++ b/Lib/test/test_pyrepl.py @@ -811,6 +811,46 @@ def test_paste_not_in_paste_mode(self): reader = self.prepare_reader(events) output = multiline_input(reader) self.assertEqual(output, output_code) + + def test_bracketed_paste(self): + """Test that bracketed paste using \x1b[200~ and \x1b[201~ works.""" + # fmt: off + input_code = ( + 'def a():\n' + ' for x in range(10):\n' + '\n' + ' if x%2:\n' + ' print(x)\n' + '\n' + ' else:\n' + ' pass\n' + ) + # fmt: on + + output_code = ( + 'def a():\n' + ' for x in range(10):\n' + '\n' + ' if x%2:\n' + ' print(x)\n' + '\n' + ' else:\n' + ' pass\n' + '\n' + ) + + 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, output_code) class TestReader(TestCase): From 9a7215935022044e2ddac70c405cdd407ad9d643 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 7 May 2024 13:17:25 +0100 Subject: [PATCH 2/6] fixup! gh-111201: Allow bracketed paste to work --- Lib/_pyrepl/simple_interact.py | 15 --------------- Lib/_pyrepl/unix_console.py | 11 +++++++++++ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Lib/_pyrepl/simple_interact.py b/Lib/_pyrepl/simple_interact.py index 98dcbba7f300fb..23f6ac06b0e28c 100644 --- a/Lib/_pyrepl/simple_interact.py +++ b/Lib/_pyrepl/simple_interact.py @@ -77,18 +77,6 @@ def __init__( def showtraceback(self): super().showtraceback(colorize=self.can_colorize) - - -def _enable_bracketed_paste() -> None: - sys.stdout.write("\x1b[?2004h") - sys.stdout.flush() - - -def _disable_bracketed_paste() -> None: - sys.stdout.write("\x1b[?2004l") - sys.stdout.flush() - - def run_multiline_interactive_console( mainmodule: ModuleType | None= None, future_flags: int = 0 ) -> None: @@ -147,12 +135,9 @@ def more_lines(unicodetext: str) -> bool: ps1 = getattr(sys, "ps1", ">>> ") ps2 = getattr(sys, "ps2", "... ") try: - _enable_bracketed_paste() statement = multiline_input(more_lines, ps1, ps2) except EOFError: break - finally: - _disable_bracketed_paste() if maybe_run_command(statement): continue diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index c22b1d5b5bc290..0b24ff698f8409 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -335,11 +335,14 @@ def prepare(self): self.old_sigwinch = signal.signal(signal.SIGWINCH, self.__sigwinch) except ValueError: pass + + self.__enable_bracketed_paste() def restore(self): """ Restore the console to the default state """ + self.__disable_bracketed_paste() self.__maybe_write_code(self._rmkx) self.flushoutput() tcsetattr(self.input_fd, termios.TCSADRAIN, self.__svtermstate) @@ -524,6 +527,14 @@ def clear(self): self.__move = self.__move_tall self.__posxy = 0, 0 self.screen = [] + + def __enable_bracketed_paste(self) -> None: + os.write(self.output_fd, b"\x1b[?2004h") + self.flushoutput() + + def __disable_bracketed_paste(self) -> None: + os.write(self.output_fd, b"\x1b[?2004l") + self.flushoutput() def __setup_movement(self): """ From ec721764ccf827814c5a39344c2e1a9573113f2c Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 7 May 2024 13:18:19 +0100 Subject: [PATCH 3/6] fixup! fixup! gh-111201: Allow bracketed paste to work --- Lib/_pyrepl/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index 9c11cf87abcf16..ccf5c288189f52 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -471,4 +471,4 @@ def do(self) -> None: class disable_bracketed_paste(Command): def do(self) -> None: self.reader.paste_mode = False - self.reader.insert("\n") \ No newline at end of file + self.reader.insert("\n") From 75222d1bc6e549d5c99ffd25fccf3e6c0bb5637a Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 7 May 2024 13:18:40 +0100 Subject: [PATCH 4/6] fixup! fixup! fixup! gh-111201: Allow bracketed paste to work --- Lib/_pyrepl/simple_interact.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/_pyrepl/simple_interact.py b/Lib/_pyrepl/simple_interact.py index 23f6ac06b0e28c..ce79d0d547ebce 100644 --- a/Lib/_pyrepl/simple_interact.py +++ b/Lib/_pyrepl/simple_interact.py @@ -77,6 +77,8 @@ def __init__( def showtraceback(self): super().showtraceback(colorize=self.can_colorize) + + def run_multiline_interactive_console( mainmodule: ModuleType | None= None, future_flags: int = 0 ) -> None: From 78ce02b97798fe6342797ea558bc71ccdf5f878a Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 7 May 2024 13:21:31 +0100 Subject: [PATCH 5/6] fixup! fixup! fixup! fixup! gh-111201: Allow bracketed paste to work --- Lib/_pyrepl/commands.py | 2 +- Lib/_pyrepl/unix_console.py | 4 ++-- Lib/test/test_pyrepl.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py index ccf5c288189f52..bb6bebace30ec8 100644 --- a/Lib/_pyrepl/commands.py +++ b/Lib/_pyrepl/commands.py @@ -467,7 +467,7 @@ def do(self) -> None: class enable_bracketed_paste(Command): def do(self) -> None: self.reader.paste_mode = True - + class disable_bracketed_paste(Command): def do(self) -> None: self.reader.paste_mode = False diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index 0b24ff698f8409..408f81b763dd34 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -335,7 +335,7 @@ def prepare(self): self.old_sigwinch = signal.signal(signal.SIGWINCH, self.__sigwinch) except ValueError: pass - + self.__enable_bracketed_paste() def restore(self): @@ -527,7 +527,7 @@ def clear(self): self.__move = self.__move_tall self.__posxy = 0, 0 self.screen = [] - + def __enable_bracketed_paste(self) -> None: os.write(self.output_fd, b"\x1b[?2004h") self.flushoutput() diff --git a/Lib/test/test_pyrepl.py b/Lib/test/test_pyrepl.py index 82e2a2569a270a..3e458baab61d34 100644 --- a/Lib/test/test_pyrepl.py +++ b/Lib/test/test_pyrepl.py @@ -811,7 +811,7 @@ def test_paste_not_in_paste_mode(self): reader = self.prepare_reader(events) output = multiline_input(reader) self.assertEqual(output, output_code) - + def test_bracketed_paste(self): """Test that bracketed paste using \x1b[200~ and \x1b[201~ works.""" # fmt: off From c0cd485b65906db2d98d139d43f01e668b0b5de9 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Tue, 7 May 2024 13:29:55 +0100 Subject: [PATCH 6/6] fixup! fixup! fixup! fixup! fixup! gh-111201: Allow bracketed paste to work --- Lib/_pyrepl/unix_console.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/_pyrepl/unix_console.py b/Lib/_pyrepl/unix_console.py index 408f81b763dd34..605318c82ae2ea 100644 --- a/Lib/_pyrepl/unix_console.py +++ b/Lib/_pyrepl/unix_console.py @@ -530,11 +530,9 @@ def clear(self): def __enable_bracketed_paste(self) -> None: os.write(self.output_fd, b"\x1b[?2004h") - self.flushoutput() def __disable_bracketed_paste(self) -> None: os.write(self.output_fd, b"\x1b[?2004l") - self.flushoutput() def __setup_movement(self): """