Ctrl-C crashes Python after importing scipy.stats
Here's a variation on your posted solution that may work. Maybe there's a better way to solve this problem -- or maybe even avoid it all together by setting an environment variable that tells the DLL to skip installing a handler. Hopefully this helps until you find a better way.
Both the time
module (lines 868-876) and _multiprocessing
module (lines 312-321) call SetConsoleCtrlHandler
. In the case of the time
module, its console control handler sets a Windows event, hInterruptEvent
. For the main thread, time.sleep
waits on this event via WaitForSingleObject(hInterruptEvent, ul_millis)
, where ul_millis
is the number of milliseconds to sleep unless interrupted by Ctrl+C. Since the handler that you've installed returns True
, the time
module's handler never gets called to set hInterruptEvent
, which means sleep
cannot be interrupted.
I tried using imp.init_builtin('time')
to reinitialize the time
module, but apparently SetConsoleCtrlHandler
ignores the 2nd call. It seems the handler has to be removed and then reinserted. Unfortunately, the time
module doesn't export a function for that. So, as a kludge, just make sure you import the time
module after you install your handler. Since importing scipy
also imports time
, you need to pre-load libifcoremd.dll using ctypes
to get the handlers in the right order. Finally, add a call to thread.interrupt_main
to make sure Python's SIGINT
handler gets called[1].
For example:
import os
import imp
import ctypes
import thread
import win32api
# Load the DLL manually to ensure its handler gets
# set before our handler.
basepath = imp.find_module('numpy')[1]
ctypes.CDLL(os.path.join(basepath, 'core', 'libmmd.dll'))
ctypes.CDLL(os.path.join(basepath, 'core', 'libifcoremd.dll'))
# Now set our handler for CTRL_C_EVENT. Other control event
# types will chain to the next handler.
def handler(dwCtrlType, hook_sigint=thread.interrupt_main):
if dwCtrlType == 0: # CTRL_C_EVENT
hook_sigint()
return 1 # don't chain to the next handler
return 0 # chain to the next handler
win32api.SetConsoleCtrlHandler(handler, 1)
>>> import time
>>> from scipy import stats
>>> time.sleep(10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyboardInterrupt
[1] interrupt_main
calls PyErr_SetInterrupt
. This trips Handlers[SIGINT]
and calls Py_AddPendingCall
to add checksignals_witharg
. In turn this calls PyErr_CheckSignals
. Since Handlers[SIGINT]
is tripped, this calls Handlers[SIGINT].func
. Finally, if func
is signal.default_int_handler
, you'll get a KeyboardInterrupt
exception.
Setting the environment variable FOR_DISABLE_CONSOLE_CTRL_HANDLER
to 1
seems to fix the issue, but only if it is set before loading offending packages.
import os
os.environ['FOR_DISABLE_CONSOLE_CTRL_HANDLER'] = '1'
[...]
EDIT: While Ctrl+C doesn't crash python anymore, it also fails to stop the current calculation.