https://github.com/python/cpython/commit/91b48868a82821c1cbfd2245212699b57d3a24c7
commit: 91b48868a82821c1cbfd2245212699b57d3a24c7
branch: main
author: Chris Eibl <138194463+chris-e...@users.noreply.github.com>
committer: ambv <luk...@langa.pl>
date: 2025-05-25T15:17:43+02:00
summary:

GH-130328: Speedup pasting in legacy console on Windows (gh-133728)

files:
A Misc/NEWS.d/next/Library/2025-05-09-09-10-34.gh-issue-130328.s9h4By.rst
M Lib/_pyrepl/commands.py
M Lib/_pyrepl/windows_console.py
M Lib/test/test_pyrepl/test_unix_console.py
M Lib/test/test_pyrepl/test_windows_console.py

diff --git a/Lib/_pyrepl/commands.py b/Lib/_pyrepl/commands.py
index 2354fbb2ec2c1e..50c824995d85b8 100644
--- a/Lib/_pyrepl/commands.py
+++ b/Lib/_pyrepl/commands.py
@@ -370,6 +370,13 @@ def do(self) -> None:
         r = self.reader
         text = self.event * r.get_arg()
         r.insert(text)
+        if r.paste_mode:
+            data = ""
+            ev = r.console.getpending()
+            data += ev.data
+            if data:
+                r.insert(data)
+                r.last_refresh_cache.invalidated = True
 
 
 class insert_nl(EditCommand):
@@ -484,7 +491,6 @@ def do(self) -> None:
         data = ""
         start = time.time()
         while done not in data:
-            self.reader.console.wait(100)
             ev = self.reader.console.getpending()
             data += ev.data
         trace(
diff --git a/Lib/_pyrepl/windows_console.py b/Lib/_pyrepl/windows_console.py
index 95749198b3b2f9..c56dcd6d7dd434 100644
--- a/Lib/_pyrepl/windows_console.py
+++ b/Lib/_pyrepl/windows_console.py
@@ -419,10 +419,7 @@ def _getscrollbacksize(self) -> int:
 
         return info.srWindow.Bottom  # type: ignore[no-any-return]
 
-    def _read_input(self, block: bool = True) -> INPUT_RECORD | None:
-        if not block and not self.wait(timeout=0):
-            return None
-
+    def _read_input(self) -> INPUT_RECORD | None:
         rec = INPUT_RECORD()
         read = DWORD()
         if not ReadConsoleInput(InHandle, rec, 1, read):
@@ -431,14 +428,10 @@ def _read_input(self, block: bool = True) -> INPUT_RECORD 
| None:
         return rec
 
     def _read_input_bulk(
-        self, block: bool, n: int
+        self, n: int
     ) -> tuple[ctypes.Array[INPUT_RECORD], int]:
         rec = (n * INPUT_RECORD)()
         read = DWORD()
-
-        if not block and not self.wait(timeout=0):
-            return rec, 0
-
         if not ReadConsoleInput(InHandle, rec, n, read):
             raise WinError(GetLastError())
 
@@ -449,8 +442,11 @@ def get_event(self, block: bool = True) -> Event | None:
         and there is no event pending, otherwise waits for the
         completion of an event."""
 
+        if not block and not self.wait(timeout=0):
+            return None
+
         while self.event_queue.empty():
-            rec = self._read_input(block)
+            rec = self._read_input()
             if rec is None:
                 return None
 
@@ -551,12 +547,20 @@ def getpending(self) -> Event:
             if e2:
                 e.data += e2.data
 
-        recs, rec_count = self._read_input_bulk(False, 1024)
+        recs, rec_count = self._read_input_bulk(1024)
         for i in range(rec_count):
             rec = recs[i]
+            # In case of a legacy console, we do not only receive a keydown
+            # event, but also a keyup event - and for uppercase letters
+            # an additional SHIFT_PRESSED event.
             if rec and rec.EventType == KEY_EVENT:
                 key_event = rec.Event.KeyEvent
+                if not key_event.bKeyDown:
+                    continue
                 ch = key_event.uChar.UnicodeChar
+                if ch == "\x00":
+                    # ignore SHIFT_PRESSED and special keys
+                    continue
                 if ch == "\r":
                     ch += "\n"
                 e.data += ch
diff --git a/Lib/test/test_pyrepl/test_unix_console.py 
b/Lib/test/test_pyrepl/test_unix_console.py
index c447b310c49a06..b3f7dc028fe210 100644
--- a/Lib/test/test_pyrepl/test_unix_console.py
+++ b/Lib/test/test_pyrepl/test_unix_console.py
@@ -20,6 +20,7 @@
 def unix_console(events, **kwargs):
     console = UnixConsole()
     console.get_event = MagicMock(side_effect=events)
+    console.getpending = MagicMock(return_value=Event("key", ""))
 
     height = kwargs.get("height", 25)
     width = kwargs.get("width", 80)
diff --git a/Lib/test/test_pyrepl/test_windows_console.py 
b/Lib/test/test_pyrepl/test_windows_console.py
index e7bab226b31ddf..a52ae96a83ddde 100644
--- a/Lib/test/test_pyrepl/test_windows_console.py
+++ b/Lib/test/test_pyrepl/test_windows_console.py
@@ -35,6 +35,7 @@ class WindowsConsoleTests(TestCase):
     def console(self, events, **kwargs) -> Console:
         console = WindowsConsole()
         console.get_event = MagicMock(side_effect=events)
+        console.getpending = MagicMock(return_value=Event("key", ""))
         console.wait = MagicMock()
         console._scroll = MagicMock()
         console._hide_cursor = MagicMock()
diff --git 
a/Misc/NEWS.d/next/Library/2025-05-09-09-10-34.gh-issue-130328.s9h4By.rst 
b/Misc/NEWS.d/next/Library/2025-05-09-09-10-34.gh-issue-130328.s9h4By.rst
new file mode 100644
index 00000000000000..00b556c6a336ee
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-05-09-09-10-34.gh-issue-130328.s9h4By.rst
@@ -0,0 +1,2 @@
+Speedup pasting in ``PyREPL`` on Windows in a legacy console. Patch by Chris
+Eibl.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to