https://github.com/python/cpython/commit/576f556e8180dffcfd934cc3540cea4cdb7a4d85
commit: 576f556e8180dffcfd934cc3540cea4cdb7a4d85
branch: 3.14
author: Miss Islington (bot) <[email protected]>
committer: vstinner <[email protected]>
date: 2026-11-08T20:33:18Z
summary:

[3.14] gh-132888: Fix Windows API error checking in pyrepl.windows_console 
(GH-144248) (#161633)

gh-132888: Fix Windows API error checking in pyrepl.windows_console (GH-144248)
(cherry picked from commit 3abc03c8088b77d9da6b74a2092f3ae05e70df01)

Co-authored-by: AN Long <[email protected]>

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 09be054183eaf90..6e3b7074d66a5b0 100644
--- a/Lib/_pyrepl/windows_console.py
+++ b/Lib/_pyrepl/windows_console.py
@@ -46,14 +46,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
 
@@ -152,16 +149,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
@@ -311,7 +310,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")
@@ -345,7 +344,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:
@@ -361,7 +360,8 @@ 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:
@@ -369,7 +369,8 @@ def restore(self) -> None:
             self.__write(ANSIColors.RESET)
             # 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"""
@@ -406,7 +407,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,
@@ -415,7 +416,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]
 
@@ -423,7 +424,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
 
@@ -433,7 +434,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
 
@@ -536,7 +537,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 54905e605f3ff8e..4f1b9734d3071f6 100644
--- a/Lib/test/test_pyrepl/test_windows_console.py
+++ b/Lib/test/test_pyrepl/test_windows_console.py
@@ -31,7 +31,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()
@@ -390,6 +404,7 @@ def test_reset_on_restore(self):
         con.out.write.assert_any_call(ANSIColors.RESET.encode(con.encoding))
 
 
[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 000000000000000..71b984c69c5c29b
--- /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]

Reply via email to