python / cpython

The Python programming language
https://www.python.org
Other
63.07k stars 30.21k forks source link

Ctrl-c not behaving as expected #95737

Open Fakesum opened 2 years ago

Fakesum commented 2 years ago

Bug report

ctrl-c does not raise a keyboard interrupt. Problem started when modifed VS 2022 installation to include windows 10 sdk from the visual studio installer. I have already tried to repair and reinstall python and restart my computer but it the problem stays the same. when python is run in shell mode it just exits when ctrl-c is pressed but does not raise the KeyboardInterrupt when ctrl-c is pressed in script without exiting.

Your environment

https://user-images.githubusercontent.com/94297407/183242398-c253b51d-9997-4318-ae65-bb2a7df739da.mp4

eryksun commented 2 years ago

It will help to know what terminal you're using -- classic console, Windows Terminal, ConEmu, something else?

eryksun commented 2 years ago

Please try the following script to find out whether typing Ctrl-C is sending the console control event CTRL_C_EVENT (0). Typing Ctrl-Break (if available on your keyboard) should send CTRL_BREAK_EVENT (1) and exit.

import time
import ctypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

CTRL_C_EVENT = 0

@ctypes.WINFUNCTYPE(ctypes.c_ulong, ctypes.c_ulong)
def handler(event):
    print('event:', event)
    if event != CTRL_C_EVENT:
        return False 
    return True

print('Type Ctrl-Break to exit.')
kernel32.SetConsoleCtrlHandler(handler, True)
while True:
    time.sleep(1)
Fakesum commented 2 years ago

I am using the standard windows cmd and I don't have break key on my keyboard.When runing the script it does not respond to ctrl-c.

eryksun commented 2 years ago

Running the CMD shell from Explorer allocates a classic console (i.e. an instance of "conhost.exe"). It's the inherited console that matters here, not the shell.

It seems that the Ctrl-C event is disabled in the Python process. This process state is either inherited from the parent process or enabled by creating a process in a new process group (i.e. the CREATE_NEW_PROCESS_GROUP process creation flag). The CMD shell's start command creates a process in a new group when the /b option is used. This option is meant to run a background process that shouldn't be interrupted by Ctrl-C.

Here's a modified version of the above script that manually enables Ctrl-C via SetConsoleCtrlHandler(). If I'm right, this script should print "event: 0" on a new line each time that Ctrl-C is pressed.

import time
import ctypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

CTRL_C_EVENT = 0

# Enable CTRL_C_EVENT for the current process.
kernel32.SetConsoleCtrlHandler(None, False)

@ctypes.WINFUNCTYPE(ctypes.c_ulong, ctypes.c_ulong)
def handler(event):
    print('event:', event)
    if event != CTRL_C_EVENT:
        return False 
    return True

kernel32.SetConsoleCtrlHandler(handler, True)

print('Type Ctrl-Break to exit.')
while True:
    time.sleep(1)

On a related note, if the Ctrl-C event is disabled, Python's console read has a bug. In this case, the canceled read gets mistakenly handled as an empty read (i.e. EOF), which is why the REPL exits. We know that typing Ctrl-C canceled the read in the console and set the last error to ERROR_OPERATION_ABORTED, so there's no reason to confuse it with EOF.

Fakesum commented 2 years ago

uhmm... The Problem solved itself? after restarting again it is now raising a KeyboardInterrupt, and the script above works fine

Fakesum commented 2 years ago

it seems that my script is disabling Ctrl-c somehow, but why would that stay even after python is reinstalled and all python processes are killed? . The Script Above does print 'event: 0', Is it suppose to exit? if so it doesn't

eryksun commented 2 years ago

it seems that my script is disabling Ctrl-c somehow, but why would that stay even after python is reinstalled and all python processes are killed?

The Ctrl+C event can be disabled for a process in three ways:

The Script Above does print 'event: 0', Is it suppose to exit? if so it doesn't

No, it's an infinite loop. Desktop keyboards have a pause/break key, in which case Ctrl-Break sends CTRL_BREAK_EVENT (1), for which the default handler calls ExitProcess(). Most laptops have some way to type the break key, such as Fn-B, in which case use Fn-Ctrl-B or maybe just Fn-B. See keyboards without break key.