python - 导入 scipy.stats 后 Ctrl-C 使 Python 崩溃

标签 python scipy keyboardinterrupt

我在 Win7 64 位上运行 64 位 Python 2.7.3。我可以通过这样做可靠地使 Python 解释器崩溃:

>>> from scipy import stats
>>> import time
>>> time.sleep(3)

并在 sleep 期间按 Control-C。没有引发 KeyboardInterrupt;解释器崩溃。打印如下:

forrtl: error (200): program aborting due to control-C event
Image              PC                Routine            Line        Source

libifcoremd.dll    00000000045031F8  Unknown               Unknown  Unknown
libifcoremd.dll    00000000044FC789  Unknown               Unknown  Unknown
libifcoremd.dll    00000000044E8583  Unknown               Unknown  Unknown
libifcoremd.dll    000000000445725D  Unknown               Unknown  Unknown
libifcoremd.dll    00000000044672A6  Unknown               Unknown  Unknown
kernel32.dll       0000000077B74AF3  Unknown               Unknown  Unknown
kernel32.dll       0000000077B3F56D  Unknown               Unknown  Unknown
ntdll.dll          0000000077C73281  Unknown               Unknown  Unknown

这使得无法中断长时间运行的 scipy 计算。

谷歌搜索“forrtl”等,我看到这种问题是由于使用了覆盖 Ctrl-C 处理的 Fortran 库引起的。我在 Scipy 跟踪器上没有看到错误,但鉴于 Scipy 是一个与 Python 一起使用的库,我认为这是一个错误。它打破了 Python 对 Ctrl-C 的处理。有什么解决方法吗?

编辑:按照@cgohlke 的建议,我尝试在导入 scipy 后添加自己的处理程序。 This question关于相关问题表明添加信号处理程序不起作用。我尝试使用 Windows API SetConsoleCtrlHandler通过pywin32函数:

from scipy import stats
import win32api
def doSaneThing(sig, func=None):
    print "Here I am"
    raise KeyboardInterrupt
win32api.SetConsoleCtrlHandler(doSaneThing, 1)

在此之后,按 Ctrl-C 会打印“我在这里”,但 Python 仍然会因 forrtl 错误而崩溃。有时我也会收到一条消息说“ConsoleCtrlHandler 功能失败”,然后很快就消失了。

如果我在 IPython 中运行它,我可以在 forrtl 错误之前看到一个正常的 Python KeyboardInterrupt 回溯。如果我引发其他错误而不是 KeyboardInterrupt(例如 ValueError),我还会看到一个正常的 Python 回溯,然后是 forrtl 错误:

ValueError                                Traceback (most recent call last)
<ipython-input-1-08defde66fcb> in doSaneThing(sig, func)
      3 def doSaneThing(sig, func=None):
      4     print "Here I am"
----> 5     raise ValueError
      6 win32api.SetConsoleCtrlHandler(doSaneThing, 1)

ValueError:
forrtl: error (200): program aborting due to control-C event
[etc.]

似乎无论底层处理程序在做什么,它不仅仅是直接捕获 Ctrl-C,而是对错误条件 (ValueError) 使用react并自行崩溃。有什么办法可以消除吗?

最佳答案

这是您发布的解决方案的一个变体,可能会起作用。也许有更好的方法来解决这个问题——或者甚至可以通过设置一个告诉 DLL 跳过安装处理程序的环境变量来避免这一切。希望这会有所帮助,直到您找到更好的方法。

time module (第 868-876 行)和 _multiprocessing module (第 312-321 行)调用 SetConsoleCtrlHandler .对于 time 模块,它的控制台控制处理程序设置一个 Windows 事件 hInterruptEvent。对于主线程,time.sleep 通过 WaitForSingleObject(hInterruptEvent, ul_millis) 等待此事件,其中 ul_millis 是休眠的毫秒数除非被 Ctrl+C 打断。由于您安装的处理程序返回 True,因此不会调用 time 模块的处理程序来设置 hInterruptEvent,这意味着 sleep 不能被打断。

我尝试使用 imp.init_builtin('time') 重新初始化 time 模块,但显然 SetConsoleCtrlHandler 忽略了第二次调用。似乎必须删除处理程序然后重新插入。不幸的是,time 模块并没有为此导出函数。因此,作为一个杂项,只需确保在安装处理程序之后 导入 time 模块。由于导入 scipy 也会导入 time,因此您需要使用 ctypes 预加载 libifcoremd.dll 以按正确顺序获取处理程序。最后,添加对 thread.interrupt_main 的调用以确保调用 Python 的 SIGINT 处理程序[1]

例如:

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 调用 PyErr_SetInterrupt。这会触发 Handlers[SIGINT] 并调用 Py_AddPendingCall 以添加 checksignals_witharg。依次调用 PyErr_CheckSignals。由于 Handlers[SIGINT] 被触发,这将调用 Handlers[SIGINT].func。最后,如果 funcsignal.default_int_handler,你会得到一个 KeyboardInterrupt 异常。

关于python - 导入 scipy.stats 后 Ctrl-C 使 Python 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15457786/

相关文章:

python - 在django中测量图像的高度和宽度并将值存储在模型中

python - 如何高斯过滤(模糊)浮点numpy数组

python - numpy 中的矩阵乘法

python - python如何在无限循环期间处理KeyboardInterrupt?

python - 在 KDE Python 应用程序中处理 KeyboardInterrupt?

python - 在引发 tkinter 框架之前,不会处理 tkinter 键盘中断

python - 这个简单的 python 元类有什么问题?

python - 转换图像灰度python错误

python - 如何计算 python 中两个向量数组的点积?

python - 我应该使用 scipy.pi、numpy.pi 还是 math.pi?