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

This commit is contained in:
Chris Eibl 2025-05-25 15:17:43 +02:00 committed by GitHub
parent 2fd09b0110
commit 91b48868a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 26 additions and 12 deletions

View file

@ -370,6 +370,13 @@ class self_insert(EditCommand):
r = self.reader r = self.reader
text = self.event * r.get_arg() text = self.event * r.get_arg()
r.insert(text) 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): class insert_nl(EditCommand):
@ -484,7 +491,6 @@ class perform_bracketed_paste(Command):
data = "" data = ""
start = time.time() start = time.time()
while done not in data: while done not in data:
self.reader.console.wait(100)
ev = self.reader.console.getpending() ev = self.reader.console.getpending()
data += ev.data data += ev.data
trace( trace(

View file

@ -419,10 +419,7 @@ class WindowsConsole(Console):
return info.srWindow.Bottom # type: ignore[no-any-return] return info.srWindow.Bottom # type: ignore[no-any-return]
def _read_input(self, block: bool = True) -> INPUT_RECORD | None: def _read_input(self) -> INPUT_RECORD | None:
if not block and not self.wait(timeout=0):
return None
rec = INPUT_RECORD() rec = INPUT_RECORD()
read = DWORD() read = DWORD()
if not ReadConsoleInput(InHandle, rec, 1, read): if not ReadConsoleInput(InHandle, rec, 1, read):
@ -431,14 +428,10 @@ class WindowsConsole(Console):
return rec return rec
def _read_input_bulk( def _read_input_bulk(
self, block: bool, n: int self, n: int
) -> tuple[ctypes.Array[INPUT_RECORD], int]: ) -> tuple[ctypes.Array[INPUT_RECORD], int]:
rec = (n * INPUT_RECORD)() rec = (n * INPUT_RECORD)()
read = DWORD() read = DWORD()
if not block and not self.wait(timeout=0):
return rec, 0
if not ReadConsoleInput(InHandle, rec, n, read): if not ReadConsoleInput(InHandle, rec, n, read):
raise WinError(GetLastError()) raise WinError(GetLastError())
@ -449,8 +442,11 @@ class WindowsConsole(Console):
and there is no event pending, otherwise waits for the and there is no event pending, otherwise waits for the
completion of an event.""" completion of an event."""
if not block and not self.wait(timeout=0):
return None
while self.event_queue.empty(): while self.event_queue.empty():
rec = self._read_input(block) rec = self._read_input()
if rec is None: if rec is None:
return None return None
@ -551,12 +547,20 @@ class WindowsConsole(Console):
if e2: if e2:
e.data += e2.data 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): for i in range(rec_count):
rec = recs[i] 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: if rec and rec.EventType == KEY_EVENT:
key_event = rec.Event.KeyEvent key_event = rec.Event.KeyEvent
if not key_event.bKeyDown:
continue
ch = key_event.uChar.UnicodeChar ch = key_event.uChar.UnicodeChar
if ch == "\x00":
# ignore SHIFT_PRESSED and special keys
continue
if ch == "\r": if ch == "\r":
ch += "\n" ch += "\n"
e.data += ch e.data += ch

View file

@ -20,6 +20,7 @@ except ImportError:
def unix_console(events, **kwargs): def unix_console(events, **kwargs):
console = UnixConsole() console = UnixConsole()
console.get_event = MagicMock(side_effect=events) console.get_event = MagicMock(side_effect=events)
console.getpending = MagicMock(return_value=Event("key", ""))
height = kwargs.get("height", 25) height = kwargs.get("height", 25)
width = kwargs.get("width", 80) width = kwargs.get("width", 80)

View file

@ -35,6 +35,7 @@ class WindowsConsoleTests(TestCase):
def console(self, events, **kwargs) -> Console: def console(self, events, **kwargs) -> Console:
console = WindowsConsole() console = WindowsConsole()
console.get_event = MagicMock(side_effect=events) console.get_event = MagicMock(side_effect=events)
console.getpending = MagicMock(return_value=Event("key", ""))
console.wait = MagicMock() console.wait = MagicMock()
console._scroll = MagicMock() console._scroll = MagicMock()
console._hide_cursor = MagicMock() console._hide_cursor = MagicMock()

View file

@ -0,0 +1,2 @@
Speedup pasting in ``PyREPL`` on Windows in a legacy console. Patch by Chris
Eibl.