gh-72353: Continue reading from the console after ignored Ctrl+C (GH-155434)
If the console cancels a read because of Ctrl+C, but the SIGINT handler
does not raise an exception, restart the read instead of ending it as if
at end of file.
Co-authored-by: Valeriya Sinevich <valeriya.sinevich@phystech.edu>
Co-authored-by: Zackery Spytz <zspytz@gmail.com>
diff --git a/Misc/NEWS.d/next/Windows/2026-08-09-14-20-00.gh-issue-72353.Vw7Kq2.rst b/Misc/NEWS.d/next/Windows/2026-08-09-14-20-00.gh-issue-72353.Vw7Kq2.rst
new file mode 100644
index 0000000..95c7718
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2026-08-09-14-20-00.gh-issue-72353.Vw7Kq2.rst
@@ -0,0 +1,3 @@
+Reading from the console on Windows now continues if the read was cancelled
+by Ctrl+C, but the :const:`~signal.SIGINT` handler did not raise an
+exception. Previously the read ended as if at end of file.
diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c
index 4cd7109..e96c5bd 100644
--- a/Modules/_io/winconsoleio.c
+++ b/Modules/_io/winconsoleio.c
@@ -644,8 +644,8 @@ read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
break;
err = 0;
HANDLE hInterruptEvent = _PyOS_SigintEvent();
- if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
- == WAIT_OBJECT_0) {
+ DWORD state = WaitForSingleObjectEx(hInterruptEvent, 100, FALSE);
+ if (state == WAIT_OBJECT_0) {
ResetEvent(hInterruptEvent);
Py_BLOCK_THREADS
sig = PyErr_CheckSignals();
@@ -653,6 +653,13 @@ read_console_w(HANDLE handle, DWORD maxlen, DWORD *readlen) {
if (sig < 0)
break;
}
+ else if (state != WAIT_TIMEOUT) {
+ err = GetLastError();
+ break;
+ }
+ /* The console cancelled the read and flushed its input buffer,
+ but no exception was raised. Start the read over. */
+ continue;
}
*readlen += n;
diff --git a/Parser/myreadline.c b/Parser/myreadline.c
index 457b959..339f55a 100644
--- a/Parser/myreadline.c
+++ b/Parser/myreadline.c
@@ -162,8 +162,8 @@ _PyOS_WindowsConsoleReadline(PyThreadState *tstate, HANDLE hStdIn)
goto exit;
err = 0;
HANDLE hInterruptEvent = _PyOS_SigintEvent();
- if (WaitForSingleObjectEx(hInterruptEvent, 100, FALSE)
- == WAIT_OBJECT_0) {
+ DWORD state = WaitForSingleObjectEx(hInterruptEvent, 100, FALSE);
+ if (state == WAIT_OBJECT_0) {
ResetEvent(hInterruptEvent);
PyEval_RestoreThread(tstate);
s = PyErr_CheckSignals();
@@ -172,7 +172,13 @@ _PyOS_WindowsConsoleReadline(PyThreadState *tstate, HANDLE hStdIn)
goto exit;
}
}
- break;
+ else if (state != WAIT_TIMEOUT) {
+ err = GetLastError();
+ goto exit;
+ }
+ /* The console cancelled the read and flushed its input buffer,
+ but no exception was raised. Start the read over. */
+ continue;
}
total_read += n_read;