This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients William.Schwartz, eryksun, ncoghlan, paul.moore, steve.dower, tim.golden, vstinner, zach.ware
Date 2021-01-19.20:38:35
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <>
> Should there be a `return NULL;` between these two lines?

In 3.x, os.kill() has always fallen back on TerminateProcess() when GenerateConsoleCtrlEvent() fails, and I assumed that was intentional. But I misunderstood that it's not actually chaining the exception via PyErr_Fetch and _PyErr_ChainExceptions, so it still fails with a SystemError when both GenerateConsoleCtrlEvent() and TerminateProcess() fail -- e.g. os.kill(4, 1). 

I checked the history for bpo-1220212. It appears that win32_kill() was committed to the 2.x branch first with a missing return statement, which was fixed a day later:

A few days later it was ported to 3.x without that fix:

If Steve Dower or Victor Stinner is okay with changing the behavior to agree with 2.x -- after more than a decade -- then I say just add the missing return statement. In that case, if os.kill() is called with a sig value of CTRL_C_EVENT (0) or CTRL_BREAK_EVENT (1), then it will only try GenerateConsoleCtrlEvent() and no longer fall back on TerminateProcess(). 

That said, it's common to call TerminateProcess() with an exit status of 1. Maybe the enum values of signal.CTRL_C_EVENT and signal.CTRL_BREAK_EVENT can be checked by identity instead of checking the integer value. That way os.kill(pid, 1) can still be used to forcefully terminate an arbitrary process with an exit status of 1.


To reiterate previous comments, the sig values of signal.CTRL_C_EVENT and signal.CTRL_BREAK_EVENT are not supported for an individual, arbitrary process. To use them, the pid value must be a process group ID, such as the pid of a process created with the flag CREATE_NEW_PROCESS_GROUP, and the process must be in the same console session as the current process. Anything else either fails (assuming no fallback on TerminateProcess) or invokes buggy behavior in the console, which can even leave the console in a broken state.

For a new process group, the cancel event is initially ignored, but the break event is always handled. To enable the cancel event, the process must call SetConsoleCtrlHandler(NULL, FALSE), such as via ctypes with kernel32.SetConsoleCtrlHandler(None, False). I think the signal module should provide a function to enable/disable Ctrl+C handling without ctypes, and implicitly enable it when setting a new SIGINT handler.

Hypothetical la la land...

I wish os.kill() in Windows had been implemented to conform with Unix kill(), and that CTRL_C_EVENT and CTRL_BREAK_EVENT were not added since they are not signals. Route negative pid values to the process-group branch that calls GenerateConsoleCtrlEvent(), and special case pid -1 to send the event to all accessible processes in the console session. Support SIGINT and SIGBREAK sig values in this case, respectively mapped to the console's cancel and break events. Don't support pid 0 in Windows, since there's no documented function to get the process group ID of the current process. Route positive pid values to the TerminateProcess() branch. Special case the sig value 0 to test for existence and access via OpenProcess(). Also, the latter should only request PROCESS_TERMINATE access, not PROCESS_ALL_ACCESS. It's common to have terminate access but not all access, e.g. to an elevated process in the current desktop session.
Date User Action Args
2021-01-19 20:38:35eryksunsetrecipients: + eryksun, paul.moore, ncoghlan, vstinner, tim.golden, zach.ware, William.Schwartz, steve.dower
2021-01-19 20:38:35eryksunsetmessageid: <>
2021-01-19 20:38:35eryksunlinkissue42962 messages
2021-01-19 20:38:35eryksuncreate