我在Windows上编写python 2.6.6代码,如下所示:
try:
dostuff()
except KeyboardInterrupt:
print "Interrupted!"
except:
print "Some other exception?"
finally:
print "cleaning up...."
print "done."
dostuff()
是一个永远循环的函数,一次从输入流读取一行并对其进行操作。我希望能够在我按下ctrl-c时停止并清理。
正在发生的事情是except KeyboardInterrupt:
下的代码根本没有运行。打印的唯一内容是“清理......”,然后打印出如下所示的追溯:
Traceback (most recent call last):
File "filename.py", line 119, in <module>
print 'cleaning up...'
KeyboardInterrupt
因此,异常处理代码没有运行,并且traceback声称在finally子句期间发生了键盘输入,这没有意义,因为命中ctrl-c是导致该部分运行的原因首先!即使是通用的except:
子句也没有运行。
编辑:根据评论,我用sys.stdin.read()替换了try:
块的内容。问题仍然与描述完全相同,finally:
块的第一行正在运行,然后打印相同的回溯。
编辑#2: 如果我在读取后添加了很多东西,那么处理程序就可以了。所以,这失败了:
try:
sys.stdin.read()
except KeyboardInterrupt:
...
但这有效:
try:
sys.stdin.read()
print "Done reading."
except KeyboardInterrupt:
...
这是打印的内容:
Done reading. Interrupted!
cleaning up...
done.
因此,出于某种原因,“完成阅读”。即使前一行发生异常,也会打印行。这不是一个真正的问题 - 显然我必须能够在“try”块内的任何地方处理异常。但是,打印不能正常工作 - 它不会像之前那样打印换行符! “Interruped”印在同一条线上......前面有一个空格,出于某种原因......?无论如何,在那之后代码完成它应该做的事情。
在我看来,这是在阻塞系统调用期间处理中断的错误。
答案 0 :(得分:21)
遗憾的是,异步异常处理不可靠(信号处理程序引发的异常,通过C API在外部上下文中引发的异常等)。如果代码中有一些关于哪些代码负责捕获它们的协调,则可以增加正确处理异步异常的机会(除了非常关键的函数之外,调用堆栈中最高可能是合适的)。
被调用函数(dostuff
)或函数向下的函数本身可能有一个您没有/无法解释的KeyboardInterrupt或BaseException的catch。
这个简单的案例适用于python 2.6.6(x64)交互式+ Windows 7(64位):
>>> 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时,有趣的事情发生了:
>>> 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
所以这有点解释了为什么在本例中执行finally语句的上下文中引发了KeyboardInterrupt:
>>> 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()调用会看到信号和bails,但是在取消注册它的处理程序后它会重新引发信号。在没有检查解释器代码库的情况下,我不确定。
这就是为什么我一般不愿意使用异步异常....
编辑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()”函数的正确模拟。 (可能会有多个错误)
答案 1 :(得分:1)
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!"
似乎没有问题。
{4}是否从标准输入读取?
答案 2 :(得分:1)
遇到类似的问题,这是我的解决方法:
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()
答案 3 :(得分:0)
以下是对正在发生的事情的猜测:
答案 4 :(得分:0)
这对我有用:
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."
答案 5 :(得分:0)
def foo():
try:
x=0
while 1:
x+=1
print (x)
except KeyboardInterrupt:
print ("interrupted!!")
foo()
工作正常。