From c7d62816b1da5b468873f5832666fd0e23047a3b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 9 Aug 2026 15:50:43 +0300 Subject: [PATCH] gh-72353: Continue reading from the console after ignored Ctrl+C 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 Co-authored-by: Zackery Spytz --- .../2026-08-09-14-20-00.gh-issue-72353.Vw7Kq2.rst | 3 +++ Modules/_io/winconsoleio.c | 11 +++++++++-- Parser/myreadline.c | 12 +++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Windows/2026-08-09-14-20-00.gh-issue-72353.Vw7Kq2.rst 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 00000000000000..95c771823ab77f --- /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 4cd71094e8f459..e96c5bd738fe0a 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 ee77479ba7bdcc..af77f986255b0f 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;