https://github.com/python/cpython/commit/3abc03c8088b77d9da6b74a2092f3ae05e70df01
commit: 3abc03c8088b77d9da6b74a2092f3ae05e70df01
branch: main
author: AN Long <[email protected]>
committer: vstinner <[email protected]>
date: 2026-02-03T13:25:29+01:00
summary:
gh-132888: Fix Windows API error checking in pyrepl.windows_console (#144248)
files:
A Misc/NEWS.d/next/Library/2026-01-27-00-03-41.gh-issue-132888.yhTfUN.rst
M Lib/_pyrepl/windows_console.py
M Lib/test/test_pyrepl/test_windows_console.py
diff --git a/Lib/_pyrepl/windows_console.py b/Lib/_pyrepl/windows_console.py
index 303af8a354ff00..6c949c046875f3 100644
--- a/Lib/_pyrepl/windows_console.py
+++ b/Lib/_pyrepl/windows_console.py
@@ -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
@@ -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
@@ -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")
@@ -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:
@@ -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"""
@@ -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,
@@ -403,7 +404,7 @@ 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]
@@ -411,7 +412,7 @@ 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
@@ -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
@@ -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
diff --git a/Lib/test/test_pyrepl/test_windows_console.py
b/Lib/test/test_pyrepl/test_windows_console.py
index 3587b834f3cd07..f03f84e0985c1f 100644
--- a/Lib/test/test_pyrepl/test_windows_console.py
+++ b/Lib/test/test_pyrepl/test_windows_console.py
@@ -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
@@ -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
[email protected](WindowsConsole, '__init__', _mock_console_init)
class WindowsConsoleTests(TestCase):
def console(self, events, **kwargs) -> Console:
console = WindowsConsole()
@@ -373,6 +387,7 @@ def test_multiline_ctrl_z(self):
con.restore()
[email protected](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
diff --git
a/Misc/NEWS.d/next/Library/2026-01-27-00-03-41.gh-issue-132888.yhTfUN.rst
b/Misc/NEWS.d/next/Library/2026-01-27-00-03-41.gh-issue-132888.yhTfUN.rst
new file mode 100644
index 00000000000000..71b984c69c5c29
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-01-27-00-03-41.gh-issue-132888.yhTfUN.rst
@@ -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`.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]