Skip to content

Commit acb222c

Browse files
authored
GH-130328: pasting in new REPL is slow on Windows (GH-132884)
1 parent ae37f3d commit acb222c

File tree

2 files changed

+28
-21
lines changed

2 files changed

+28
-21
lines changed

Lib/_pyrepl/windows_console.py

+27-21
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@
2222
import io
2323
import os
2424
import sys
25-
import time
26-
import msvcrt
2725

2826
import ctypes
2927
from ctypes.wintypes import (
@@ -44,14 +42,17 @@
4442
from .windows_eventqueue import EventQueue
4543

4644
try:
47-
from ctypes import GetLastError, WinDLL, windll, WinError # type: ignore[attr-defined]
45+
from ctypes import get_last_error, GetLastError, WinDLL, windll, WinError # type: ignore[attr-defined]
4846
except:
4947
# Keep MyPy happy off Windows
5048
from ctypes import CDLL as WinDLL, cdll as windll
5149

5250
def GetLastError() -> int:
5351
return 42
5452

53+
def get_last_error() -> int:
54+
return 42
55+
5556
class WinError(OSError): # type: ignore[no-redef]
5657
def __init__(self, err: int | None, descr: str | None = None) -> None:
5758
self.err = err
@@ -108,6 +109,12 @@ def __init__(self, err: int | None, descr: str | None = None) -> None:
108109
ALT_ACTIVE = 0x01 | 0x02
109110
CTRL_ACTIVE = 0x04 | 0x08
110111

112+
WAIT_TIMEOUT = 0x102
113+
WAIT_FAILED = 0xFFFFFFFF
114+
115+
# from winbase.h
116+
INFINITE = 0xFFFFFFFF
117+
111118

112119
class _error(Exception):
113120
pass
@@ -409,12 +416,8 @@ def _getscrollbacksize(self) -> int:
409416
return info.srWindow.Bottom # type: ignore[no-any-return]
410417

411418
def _read_input(self, block: bool = True) -> INPUT_RECORD | None:
412-
if not block:
413-
events = DWORD()
414-
if not GetNumberOfConsoleInputEvents(InHandle, events):
415-
raise WinError(GetLastError())
416-
if not events.value:
417-
return None
419+
if not block and not self.wait(timeout=0):
420+
return None
418421

419422
rec = INPUT_RECORD()
420423
read = DWORD()
@@ -522,14 +525,16 @@ def getpending(self) -> Event:
522525

523526
def wait(self, timeout: float | None) -> bool:
524527
"""Wait for an event."""
525-
# Poor man's Windows select loop
526-
start_time = time.time()
527-
while True:
528-
if msvcrt.kbhit(): # type: ignore[attr-defined]
529-
return True
530-
if timeout and time.time() - start_time > timeout / 1000:
531-
return False
532-
time.sleep(0.01)
528+
if timeout is None:
529+
timeout = INFINITE
530+
else:
531+
timeout = int(timeout)
532+
ret = WaitForSingleObject(InHandle, timeout)
533+
if ret == WAIT_FAILED:
534+
raise WinError(get_last_error())
535+
elif ret == WAIT_TIMEOUT:
536+
return False
537+
return True
533538

534539
def repaint(self) -> None:
535540
raise NotImplementedError("No repaint support")
@@ -649,14 +654,15 @@ class INPUT_RECORD(Structure):
649654
ReadConsoleInput.argtypes = [HANDLE, POINTER(INPUT_RECORD), DWORD, POINTER(DWORD)]
650655
ReadConsoleInput.restype = BOOL
651656

652-
GetNumberOfConsoleInputEvents = _KERNEL32.GetNumberOfConsoleInputEvents
653-
GetNumberOfConsoleInputEvents.argtypes = [HANDLE, POINTER(DWORD)]
654-
GetNumberOfConsoleInputEvents.restype = BOOL
655657

656658
FlushConsoleInputBuffer = _KERNEL32.FlushConsoleInputBuffer
657659
FlushConsoleInputBuffer.argtypes = [HANDLE]
658660
FlushConsoleInputBuffer.restype = BOOL
659661

662+
WaitForSingleObject = _KERNEL32.WaitForSingleObject
663+
WaitForSingleObject.argtypes = [HANDLE, DWORD]
664+
WaitForSingleObject.restype = DWORD
665+
660666
OutHandle = GetStdHandle(STD_OUTPUT_HANDLE)
661667
InHandle = GetStdHandle(STD_INPUT_HANDLE)
662668
else:
@@ -670,7 +676,7 @@ def _win_only(*args, **kwargs):
670676
GetConsoleMode = _win_only
671677
SetConsoleMode = _win_only
672678
ReadConsoleInput = _win_only
673-
GetNumberOfConsoleInputEvents = _win_only
674679
FlushConsoleInputBuffer = _win_only
680+
WaitForSingleObject = _win_only
675681
OutHandle = 0
676682
InHandle = 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Speedup pasting in ``PyREPL`` on Windows. Fix by Chris Eibl.

0 commit comments

Comments
 (0)