]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111201: Speed up paste mode in the REPL (#119341)
authorPablo Galindo Salgado <Pablogsal@gmail.com>
Wed, 22 May 2024 05:28:32 +0000 (01:28 -0400)
committerGitHub <noreply@github.com>
Wed, 22 May 2024 05:28:32 +0000 (07:28 +0200)
Co-authored-by: Ɓukasz Langa <lukasz@langa.pl>
Lib/_pyrepl/commands.py
Lib/_pyrepl/reader.py
Lib/_pyrepl/readline.py
Lib/_pyrepl/simple_interact.py
Lib/_pyrepl/utils.py
Lib/test/test_pyrepl/test_pyrepl.py

index 3d9722d1586c2a24dd58b2f95d766914db182518..33c6564f640421d7c2daafb81dfdbf1b1bf469b8 100644 (file)
@@ -461,8 +461,6 @@ class show_history(Command):
 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
 
@@ -470,9 +468,10 @@ class paste_mode(Command):
 class enable_bracketed_paste(Command):
     def do(self) -> None:
         self.reader.paste_mode = True
-        self.reader.was_paste_mode_activated = True
+        self.reader.in_bracketed_paste = True
 
 class disable_bracketed_paste(Command):
     def do(self) -> None:
         self.reader.paste_mode = False
+        self.reader.in_bracketed_paste = False
         self.reader.dirty = True
index 9a207a241d5f8d58afa41737f475653363721d71..3dbfc34780e8e8d75c542790cce93269abfc2989 100644 (file)
@@ -54,7 +54,7 @@ def disp_str(buffer: str) -> tuple[str, list[int]]:
     b: list[int] = []
     s: list[str] = []
     for c in buffer:
-        if unicodedata.category(c).startswith("C"):
+        if ord(c) > 128 and unicodedata.category(c).startswith("C"):
             c = r"\u%04x" % ord(c)
         s.append(c)
         b.append(wlen(c))
@@ -225,7 +225,7 @@ class Reader:
     dirty: bool = False
     finished: bool = False
     paste_mode: bool = False
-    was_paste_mode_activated: bool = False
+    in_bracketed_paste: 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)
@@ -448,7 +448,7 @@ class Reader:
         elif "\n" in self.buffer:
             if lineno == 0:
                 prompt = self.ps2
-            elif lineno == self.buffer.count("\n"):
+            elif self.ps4 and lineno == self.buffer.count("\n"):
                 prompt = self.ps4
             else:
                 prompt = self.ps3
@@ -611,7 +611,7 @@ class Reader:
 
         self.after_command(command)
 
-        if self.dirty:
+        if self.dirty and not self.in_bracketed_paste:
             self.refresh()
         else:
             self.update_cursor()
index 054a39b7442655ec34b73f2d37232f7dd28f54a4..796f1ef86360deb2dde268bd56a0498c54a21820 100644 (file)
@@ -328,7 +328,7 @@ class _ReadlineWrapper:
         reader.ps1 = str(prompt)
         return reader.readline(startup_hook=self.startup_hook)
 
-    def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) -> tuple[str, bool]:
+    def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) -> str:
         """Read an input on possibly multiple lines, asking for more
         lines as long as 'more_lines(unicodetext)' returns an object whose
         boolean value is true.
@@ -337,14 +337,15 @@ class _ReadlineWrapper:
         saved = reader.more_lines
         try:
             reader.more_lines = more_lines
-            reader.ps1 = reader.ps2 = ps1
-            reader.ps3 = reader.ps4 = ps2
+            reader.ps1 = ps1
+            reader.ps2 = ps1
+            reader.ps3 = ps2
+            reader.ps4 = ""
             with warnings.catch_warnings(action="ignore"):
-                return reader.readline(), reader.was_paste_mode_activated
+                return reader.readline()
         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
index d65b6d0d62790acf8e10b1c0635752bf21c1c8b7..8ab4dab757685ee6e85aa265eb1da422b17b797d 100644 (file)
@@ -62,6 +62,7 @@ REPL_COMMANDS = {
     "quit": _sitebuiltins.Quitter('quit' ,''),
     "copyright": _sitebuiltins._Printer('copyright', sys.copyright),
     "help": "help",
+    "clear": "clear_screen",
 }
 
 class InteractiveColoredConsole(code.InteractiveConsole):
@@ -163,7 +164,7 @@ def run_multiline_interactive_console(
             ps1 = getattr(sys, "ps1", ">>> ")
             ps2 = getattr(sys, "ps2", "... ")
             try:
-                statement, contains_pasted_code = multiline_input(more_lines, ps1, ps2)
+                statement = multiline_input(more_lines, ps1, ps2)
             except EOFError:
                 break
 
index cd1df7c49a216ddc323f081fe20213e2385ce37e..96e917e487d91a8019f110d4e2b0f8103d4507ca 100644 (file)
@@ -1,10 +1,14 @@
 import re
 import unicodedata
+import functools
 
 ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]")
 
 
+@functools.cache
 def str_width(c: str) -> int:
+    if ord(c) < 128:
+        return 1
     w = unicodedata.east_asian_width(c)
     if w in ('N', 'Na', 'H', 'A'):
         return 1
@@ -13,6 +17,6 @@ def str_width(c: str) -> int:
 
 def wlen(s: str) -> int:
     length = sum(str_width(i) for i in s)
-
     # remove lengths of any escape sequences
-    return length - sum(len(i) for i in ANSI_ESCAPE_SEQUENCE.findall(s))
+    sequence = ANSI_ESCAPE_SEQUENCE.findall(s)
+    return length - sum(len(i) for i in sequence)
index 930f6759fb0b482143bddd97b3ae234d51d83143..7b5217e4b01fd02cd250341fc1af508357805743 100644 (file)
@@ -578,7 +578,7 @@ class TestPyReplCompleter(TestCase):
         reader = self.prepare_reader(events, namespace)
         mock_get_reader.return_value = reader
         output = readline_multiline_input(more_lines, ">>>", "...")
-        self.assertEqual(output[0], "dummy.test_func.__")
+        self.assertEqual(output, "dummy.test_func.__")
         self.assertEqual(mock_stderr.getvalue(), "")