导入scipy.stats后,Ctrl-C会使Python崩溃 [英] Ctrl-C crashes Python after importing scipy.stats

查看:175
本文介绍了导入scipy.stats后,Ctrl-C会使Python崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在Win7 64位上运行64位Python 2.7.3.通过执行以下操作,我可以可靠地使Python解释器崩溃:

I'm running 64-bit Python 2.7.3 on Win7 64-bit. I can reliably crash the Python interpreter by doing this:

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

,然后在睡眠期间按Control-C.没有引发KeyboardInterrupt;解释器崩溃.打印以下内容:

and pressing Control-C during the sleep. A KeyboardInterrupt is not raised; the interpreter crashes. The following is printed:

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计算.

This makes it impossible to interrupt long-running scipy calculations.

搜索"forrtl"之类的内容,我看到这样的问题的建议是由于使用了覆盖Ctrl-C处理的Fortran库所致.我看不到Scipy跟踪器上的错误,但是鉴于Scipy是与Python一起使用的库,我认为这是一个错误.它破坏了Python对Ctrl-C的处理.有什么解决方法吗?

Googling for "forrtl" and the like, I see suggestions that this kind of problem is due to use of a Fortran library that overrides Ctrl-C handling. I don't see a bug on the Scipy trackerbut given that Scipy is a library for use with Python, I would consider this a bug. It breaks Python's handling of Ctrl-C. Is there any workaround for this?

按照@cgohlke的建议,我尝试在导入scipy之后添加自己的处理程序. 有关相关问题的问题显示添加信号处理程序不起作用.我尝试使用Windows API SetConsoleCtrlHandler 通过pywin32实现的功能:

Following @cgohlke's suggestion I tried to add my own handler after importing scipy. This question about a related issue shows that adding a signal handler doesn't work. I tried using the Windows API SetConsoleCtrlHandler function via pywin32:

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

在此之后,按Ctrl-C会打印"Here I am",但Python仍然会因forrtl错误而崩溃.有时我还会收到一条消息,提示"ConsoleCtrlHandler函数失败",该消息很快消失了.

After this, hitting Ctrl-C prints "Here I am", but Python still crashes with the forrtl error. Sometimes I also get a message saying "ConsoleCtrlHandler function failed", which quickly disappears.

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

If I run this in IPython, I can see a normal Python KeyboardInterrupt traceback before the forrtl error. I also see a normal Python traceback followed by the forrtl error if I raise some other error instead of KeyboardInterrupt (e.g., ValueError):

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)做出反应并使自身崩溃.有什么办法可以消除这种情况?

It seems that whatever the underlying handler is doing, it's not just trapping Ctrl-C directly, but is reacting to the error condition (ValueError) and crashing itself. Is there any way to eliminate this?

推荐答案

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

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.

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

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.

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

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].

例如:

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异常.


[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.

这篇关于导入scipy.stats后,Ctrl-C会使Python崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆