Ctrl-C crashes Python after importing scipy.stats Ctrl-C crashes Python after importing scipy.stats python python

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 osimport impimport ctypesimport threadimport 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 handlerwin32api.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 osos.environ['FOR_DISABLE_CONSOLE_CTRL_HANDLER'] = '1'[...]

EDIT: While Ctrl+C doesn't crash python anymore, it also fails to stop the current calculation.


I have been able to get a half-workaround by doing this:

from scipy import statsimport win32apidef doSaneThing(sig, func=None):    return Truewin32api.SetConsoleCtrlHandler(doSaneThing, 1)

Returning true in the handler stops the chain of handlers so that the meddling Fortran handler is no longer called. However, this workaround is only partial, for two reasons:

  1. It does not actually raise a KeyboardInterrupt, meaning that I can't react to it in Python code. It just drops me back to the prompt.
  2. It doesn't fully interrupt things in the way that Ctrl-C normally does in Python. If in a fresh Python session I do a time.sleep(3) and hit Ctrl-C, the sleep is immediately aborted and I get a KeyboardInterrupt. With the above workaround, the sleep is not aborted, and control returns to the prompt only after the sleep time is up.

Nonetheless, this is still better than crashing the whole session. To me this raises the question of why SciPy (and any other Python libraries that rely on these Intel libraries) don't do this themselves.

I'm leaving this answer unaccepted in the hope that someone can provide a real solution or workaround. By "real" I mean that pressing Ctrl-C during a long-running SciPy calculation should work just like it does when SciPy is not loaded. (Note that this doesn't mean it has to work immediately. Non-SciPy calculations like plain Python sum(xrange(100000000)) may not immediately abort on Ctrl-C, but at least when they do, they raise a KeyboardInterrupt.)