Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions Lib/_pyrepl/windows_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,11 @@
from .windows_eventqueue import EventQueue

try:
from ctypes import get_last_error, GetLastError, WinDLL, windll, WinError # type: ignore[attr-defined]
from ctypes import get_last_error, WinDLL, windll, WinError # type: ignore[attr-defined]
except:
# Keep MyPy happy off Windows
from ctypes import CDLL as WinDLL, cdll as windll

def GetLastError() -> int:
return 42

def get_last_error() -> int:
return 42

Expand Down Expand Up @@ -149,16 +146,18 @@ def __init__(

# Save original console modes so we can recover on cleanup.
original_input_mode = DWORD()
GetConsoleMode(InHandle, original_input_mode)
if not GetConsoleMode(InHandle, original_input_mode):
raise WinError(get_last_error())
trace(f'saved original input mode 0x{original_input_mode.value:x}')
self.__original_input_mode = original_input_mode.value

SetConsoleMode(
if not SetConsoleMode(
OutHandle,
ENABLE_WRAP_AT_EOL_OUTPUT
| ENABLE_PROCESSED_OUTPUT
| ENABLE_VIRTUAL_TERMINAL_PROCESSING,
)
):
raise WinError(get_last_error())

self.screen: list[str] = []
self.width = 80
Expand Down Expand Up @@ -301,7 +300,7 @@ def _scroll(
if not ScrollConsoleScreenBuffer(
OutHandle, scroll_rect, None, destination_origin, fill_info
):
raise WinError(GetLastError())
raise WinError(get_last_error())

def _hide_cursor(self):
self.__write("\x1b[?25l")
Expand Down Expand Up @@ -335,7 +334,7 @@ def __write(self, text: str) -> None:
def screen_xy(self) -> tuple[int, int]:
info = CONSOLE_SCREEN_BUFFER_INFO()
if not GetConsoleScreenBufferInfo(OutHandle, info):
raise WinError(GetLastError())
raise WinError(get_last_error())
return info.dwCursorPosition.X, info.dwCursorPosition.Y

def _erase_to_end(self) -> None:
Expand All @@ -350,14 +349,16 @@ def prepare(self) -> None:
self.__offset = 0

if self.__vt_support:
SetConsoleMode(InHandle, self.__original_input_mode | ENABLE_VIRTUAL_TERMINAL_INPUT)
if not SetConsoleMode(InHandle, self.__original_input_mode | ENABLE_VIRTUAL_TERMINAL_INPUT):
raise WinError(get_last_error())
self._enable_bracketed_paste()

def restore(self) -> None:
if self.__vt_support:
# Recover to original mode before running REPL
self._disable_bracketed_paste()
SetConsoleMode(InHandle, self.__original_input_mode)
if not SetConsoleMode(InHandle, self.__original_input_mode):
raise WinError(get_last_error())

def _move_relative(self, x: int, y: int) -> None:
"""Moves relative to the current posxy"""
Expand Down Expand Up @@ -394,7 +395,7 @@ def getheightwidth(self) -> tuple[int, int]:
and width of the terminal window in characters."""
info = CONSOLE_SCREEN_BUFFER_INFO()
if not GetConsoleScreenBufferInfo(OutHandle, info):
raise WinError(GetLastError())
raise WinError(get_last_error())
return (
info.srWindow.Bottom - info.srWindow.Top + 1,
info.srWindow.Right - info.srWindow.Left + 1,
Expand All @@ -403,15 +404,15 @@ def getheightwidth(self) -> tuple[int, int]:
def _getscrollbacksize(self) -> int:
info = CONSOLE_SCREEN_BUFFER_INFO()
if not GetConsoleScreenBufferInfo(OutHandle, info):
raise WinError(GetLastError())
raise WinError(get_last_error())

return info.srWindow.Bottom # type: ignore[no-any-return]

def _read_input(self) -> INPUT_RECORD | None:
rec = INPUT_RECORD()
read = DWORD()
if not ReadConsoleInput(InHandle, rec, 1, read):
raise WinError(GetLastError())
raise WinError(get_last_error())

return rec

Expand All @@ -421,7 +422,7 @@ def _read_input_bulk(
rec = (n * INPUT_RECORD)()
read = DWORD()
if not ReadConsoleInput(InHandle, rec, n, read):
raise WinError(GetLastError())
raise WinError(get_last_error())

return rec, read.value

Expand Down Expand Up @@ -523,7 +524,7 @@ def flushoutput(self) -> None:
def forgetinput(self) -> None:
"""Forget all pending, but not yet processed input."""
if not FlushConsoleInputBuffer(InHandle):
raise WinError(GetLastError())
raise WinError(get_last_error())

def getpending(self) -> Event:
"""Return the characters that have been typed but not yet
Expand Down
17 changes: 16 additions & 1 deletion Lib/test/test_pyrepl/test_windows_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from test.support import force_not_colorized_test_class
from typing import Iterable
from unittest import TestCase
from unittest.mock import MagicMock, call
from unittest.mock import MagicMock, call, patch

from .support import handle_all_events, code_to_events
from .support import prepare_reader as default_prepare_reader
Expand All @@ -30,7 +30,21 @@
pass


def _mock_console_init(self, f_in=0, f_out=1, term="", encoding="utf-8"):
"""Mock __init__ to avoid real Windows API calls in headless environments."""
super(WindowsConsole, self).__init__(f_in, f_out, term, encoding)
self.screen = []
self.width = 80
self.height = 25
self._WindowsConsole__offset = 0
self.posxy = (0, 0)
self._WindowsConsole__vt_support = False
self._WindowsConsole_original_input_mode = 0
self.event_queue = wc.EventQueue('utf-8')


@force_not_colorized_test_class
@patch.object(WindowsConsole, '__init__', _mock_console_init)
class WindowsConsoleTests(TestCase):
def console(self, events, **kwargs) -> Console:
console = WindowsConsole()
Expand Down Expand Up @@ -373,6 +387,7 @@ def test_multiline_ctrl_z(self):
con.restore()


@patch.object(WindowsConsole, '__init__', _mock_console_init)
class WindowsConsoleGetEventTests(TestCase):
# Virtual-Key Codes: https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
VK_BACK = 0x08
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix incorrect use of :func:`ctypes.GetLastError` and add missing error
checks for Windows API calls in :mod:`!_pyrepl.windows_console`.
Loading