我正在编写窗口上的Python 2.6.6代码,看起来像这样:
try: dostuff() except KeyboardInterrupt: print "Interrupted!" except: print "Some other exception?" finally: print "cleaning up...." print "done."
dostuff()
是一个永久循环的函数,每次从inputstream中读取一行并对其执行操作。 我想要能够阻止它,并清理时,我按Ctrl-C。
发生了什么事情, except KeyboardInterrupt:
的代码根本没有运行。 唯一被打印的是“清理…”,然后回溯打印,看起来像这样:
Traceback (most recent call last): File "filename.py", line 119, in <module> print 'cleaning up...' KeyboardInterrupt
所以,exception处理代码没有运行,traceback声称在finally子句中发生了KeyboardInterrupt,因为按下ctrl-c是没有意义的,这就是导致该部分首先运行的原因! 即使是通用的except:
子句也没有运行。
编辑:基于评论,我用sys.stdin.read()replace了try:
块的内容。 问题仍然如上所述发生, finally:
块的第一行运行,然后打印相同的回溯。
编辑#2:如果我添加几乎任何东西后读,处理程序的作品。 所以,这失败了:
try: sys.stdin.read() except KeyboardInterrupt: ...
但是这个工作:
try: sys.stdin.read() print "Done reading." except KeyboardInterrupt: ...
以下是打印的内容:
Done reading. Interrupted! cleaning up... done.
所以,出于某种原因,“完成阅读”。 行被打印,即使在上一行发生exception。 这不是一个真正的问题 – 显然我必须能够处理“try”块内的任何地方的exception。 然而,打印不能正常工作 – 它不会打印一个换行符像它应该! “Interruped”印在同一行上,前面有空格,出于某种原因…? 无论如何,之后的代码做它应该的。
在我看来,这是一个在阻塞的系统调用期间处理中断的错误。
异步异常处理不幸的是不可靠(信号处理程序引发的异常,通过C API的外部异常等)。 如果在代码中有一些关于负责捕获它们的代码(在调用堆栈中最高可能是非常关键的功能除外),那么你可以更好地处理异步异常的机会。
被调用的函数( dostuff
)或函数进一步向下的函数本身可能有一个KeyboardInterrupt或BaseException的catch,你没有/不能解释。
python 2.6.6(x64)interactive + Windows 7(64bit)这个简单的例子工作得很好:
>>> import time >>> def foo(): ... try: ... time.sleep(100) ... except KeyboardInterrupt: ... print "INTERRUPTED!" ... >>> foo() INTERRUPTED! #after pressing ctrl+c
编辑:
经过进一步调查,我尝试了我认为是其他人用来再现问题的例子。 我很懒,所以我把“终于”
>>> def foo(): ... try: ... sys.stdin.read() ... except KeyboardInterrupt: ... print "BLAH" ... >>> foo()
这会在按下CTRL + C后立即返回。 当我立即尝试再次打电话给富有趣的事情发生了:
>>> foo() Traceback (most recent call last): File "c:\Python26\lib\encodings\cp437.py", line 14, in decode def decode(self,input,errors='strict'): KeyboardInterrupt
立即提出异常,没有我点击CTRL + C。
这似乎是有道理的 – 似乎我们正在处理如何在Python中处理异步异常的细微差别。 在异步异常被实际弹出之前,它可能需要几个字节码指令,然后在当前的执行上下文中引发异步异常。 (这是我过去玩过的行为)
请参阅C API: http : //docs.python.org/c-api/init.html#PyThreadState_SetAsyncExc
因此,这有点解释了为什么KeyboardInterrupt在本例中的finally语句执行的上下文中被引发:
>>> def foo(): ... try: ... sys.stdin.read() ... except KeyboardInterrupt: ... print "interrupt" ... finally: ... print "FINALLY" ... >>> foo() FINALLY Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 7, in foo KeyboardInterrupt
自定义信号处理程序与解释器的标准KeyboardInterrupt / CTRL + C处理程序混合在一起可能会导致这种行为。 例如,read()调用会看到信号和提示,但是在注销它的处理程序之后它会重新提高信号。 如果不检查解释器代码库,我肯定不知道。
这就是为什么我通常回避利用异步异常….
编辑2
我认为有一个错误报告的好例子。
再次更多的理论…(只是基于阅读代码)看到文件对象来源: http : //svn.python.org/view/python/branches/release26-maint/Objects/fileobject.c?revision=81277&view=markup
file_read调用Py_UniversalNewlineFread()。 fread可以返回errno = EINTR的错误(它执行自己的信号处理)。 在这种情况下Py_UniversalNewlineFread()保留,但不执行任何信号检查PyErr_CheckSignals(),以便处理程序可以同步调用。 file_read清除文件错误,但也不会调用PyErr_CheckSignals()。
有关使用方法的示例,请参阅getline()和getline_via_fgets()。 在这个错误报告中记录了类似问题的模式:( http://bugs.python.org/issue1195 )。 所以这个信号似乎是由译员在不确定的时间处理的。
我想潜水的价值不大,因为现在还不清楚sys.stdin.read()例子是否是你的“dostuff()”函数的一个适当的类比。 (可能会有多个bug)
sys.stdin.read()
是一个系统调用,因此每个系统的行为将会不同。 对于Windows 7,我认为发生的事情是输入缓冲,所以你得到的地方是sys.stdin.read()
返回到Ctrl-C的所有内容,只要你再次访问sys.stdin,会发送“Ctrl-C”。
尝试以下,
def foo(): try: print sys.stdin.read() print sys.stdin.closed except KeyboardInterrupt: print "Interrupted!"
这表明stdin的缓冲区正在导致另一个调用stdin来识别键盘输入
def foo(): try: x=0 while 1: x += 1 print x except KeyboardInterrupt: print "Interrupted!"
似乎没有问题。
dostuff()
从标准输入读取?
有类似的问题,这是我的解决方法:
try: some_blocking_io_here() # CTRL-C to interrupt except: try: print() # any i/o will get the second KeyboardInterrupt here? except: real_handler_here()
以下是对发生的事情的猜测:
这适用于我:
import sys if __name__ == "__main__": try: sys.stdin.read() print "Here" except KeyboardInterrupt: print "Worked" except: print "Something else" finally: print "Finally"
尝试在dostuff()函数之外放置一条线,或者将循环条件移到该函数的外部。 例如:
try: while True: dostuff() except KeyboardInterrupt: print "Interrupted!" except: print "Some other exception?" finally: print "cleaning up...." print "done."
def foo(): try: x=0 while 1: x+=1 print (x) except KeyboardInterrupt: print ("interrupted!!") foo()
这工作正常。