简体   繁体   English

为什么我不能在python中处理KeyboardInterrupt?

[英]Why can't I handle a KeyboardInterrupt in python?

I'm writing python 2.6.6 code on windows that looks like this: 我在Windows上编写python 2.6.6代码,如下所示:

try:
    dostuff()
except KeyboardInterrupt:
    print "Interrupted!"
except:
    print "Some other exception?"
finally:
    print "cleaning up...."
    print "done."

dostuff() is a function that loops forever, reading a line at a time from an input stream and acting on it. dostuff()是一个永远循环的函数,一次从输入流读取一行并对其进行操作。 I want to be able to stop it and clean up when I hit ctrl-c. 我希望能够在我按下ctrl-c时停止并清理它。

What's happening instead is that the code under except KeyboardInterrupt: isn't running at all. 发生的事情是except KeyboardInterrupt:的代码根本没有运行。 The only thing that gets printed is "cleaning up...", and then a traceback is printed that looks like this: 打印的唯一内容是“清理......”,然后打印出如下所示的回溯:

Traceback (most recent call last):
  File "filename.py", line 119, in <module>
    print 'cleaning up...'
KeyboardInterrupt

So, exception handling code is NOT running, and the traceback claims that a KeyboardInterrupt occurred during the finally clause , which doesn't make sense because hitting ctrl-c is what caused that part to run in the first place! 因此,异常处理代码没有运行,并且traceback声称在finally子句期间发生了KeyboardInterrupt,这没有意义,因为命中ctrl-c是导致该部分首先运行的原因! Even the generic except: clause isn't running. 即使是通用的except:子句也没有运行。

EDIT: Based on the comments, I replaced the contents of the try: block with sys.stdin.read(). 编辑:根据评论,我用sys.stdin.read()替换了try: block的内容。 The problem still occurs exactly as described, with the first line of the finally: block running and then printing the same traceback. 问题仍然完全如所描述的那样, finally: block的第一行运行然后打印相同的回溯。

EDIT #2: If I add pretty much anything after the read, the handler works. 编辑#2:如果我在阅读后添加了很多东西,那么处理程序就可以了。 So, this fails: 所以,这失败了:

try:
    sys.stdin.read()
except KeyboardInterrupt:
    ...

But this works: 但这有效:

try:
    sys.stdin.read()
    print "Done reading."
except KeyboardInterrupt:
    ...

Here's what's printed: 这是打印的内容:

Done reading. Interrupted!
cleaning up...
done.

So, for some reason, the "Done reading." 因此,出于某种原因,“完成阅读”。 line is printed, even though the exception occurred on the previous line. 即使前一行发生异常,也会打印行。 That's not really a problem - obviously I have to be able to handle an exception anywhere inside the "try" block. 这不是一个真正的问题 - 显然我必须能够在“try”块内的任何地方处理异常。 However, the print doesn't work normally - it doesn't print a newline afterwards like it's supposed to! 但是,打印不能正常工作 - 它不会像之前那样打印换行符! The "Interruped" is printed on the same line... with a space before it, for some reason...? “Interruped”印在同一条线上......前面有一个空格,出于某种原因......? Anyway, after that the code does what it's supposed to. 无论如何,在那之后代码完成它应该做的事情。

It seems to me that this is a bug in handling an interrupt during a blocked system call. 在我看来,这是在阻塞系统调用期间处理中断的错误。

Asynchronous exception handling is unfortunately not reliable (exceptions raised by signal handlers, outside contexts via C API, etc). 遗憾的是,异步异常处理不可靠(信号处理程序引发的异常,通过C API在外部上下文中引发的异常等)。 You can increase your chances of handling the async exception properly if there is some coordination in the code about what piece of code is responsible for catching them (highest possible in the call stack seems appropriate except for very critical functions). 如果代码中有关于哪些代码负责捕获它们的协调,则可以增加正确处理异步异常的机会(除了非常关键的函数之外,调用堆栈中最高可能是合适的)。

The called function ( dostuff ) or functions further down the stack may itself have a catch for KeyboardInterrupt or BaseException that you didn't/couldn't account for. 被调用的函数( dostuff )或函数向下的函数本身可能有一个您没有/无法解释的KeyboardInterrupt或BaseException的catch。

This trivial case worked just fine with python 2.6.6 (x64) interactive + Windows 7 (64bit): 这个简单的案例适用于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

EDIT: 编辑:

Upon further investigation, I tried what I believe is the example that others have used to reproduce the issue. 经过进一步调查,我尝试了我认为是其他人用来重现问题的例子。 I was lazy so I left out the "finally" 我很懒,所以我遗漏了“终于”

>>> def foo():
...     try:
...             sys.stdin.read()
...     except KeyboardInterrupt:
...             print "BLAH"
...
>>> foo()

This returns immediately after hitting CTRL+C. 在按CTRL + C后立即返回。 The interesting thing happened when I immediately tried to call foo again: 有趣的事情发生在我立刻再次打电话给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

The exception was raised immediately without me hitting CTRL+C. 在没有按下CTRL + C的情况下立即引发异常。

This would seem to make sense - it appears that we are dealing with nuances in how asynchronous exceptions are handled in Python. 这似乎是有道理的 - 似乎我们正在处理如何在Python中处理异步异常的细微差别。 It can take several bytecode instructions before the async exception is actually popped and then raised within the current execution context. 在异步异常实际弹出然后在当前执行上下文中引发之前,它可能需要几个字节码指令。 (That's the behavior that I've seen when playing with it in the past) (这是我过去玩过时看到的行为)

See the C API: http://docs.python.org/c-api/init.html#PyThreadState_SetAsyncExc 请参阅C API: http//docs.python.org/c-api/init.html#PyThreadState_SetAsyncExc

So this somewhat explains why KeyboardInterrupt gets raised in the context of the execution of the finally statement in this example: 所以这有点解释了为什么在本例中执行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

There could be some crazy mixing of custom signal handlers mixed with the interpreter's standard KeyboardInterrupt/CTRL+C handler that's resulting in this sort of behavior. 可能会有一些疯狂混合的自定义信号处理程序与解释器的标准KeyboardInterrupt / CTRL + C处理程序混合,导致这种行为。 For example, the read() call sees the signal and bails, but it re-raises the signal after deregistering it's handler. 例如,read()调用会看到信号和bails,但是在取消注册它的处理程序后它会重新引发信号。 I wouldn't know for sure without inspecting the interpreter codebase. 在没有检查解释器代码库的情况下,我不确定。

This is why I generally shy away from making use of async exceptions.... 这就是为什么我通常不愿意使用异步异常....

EDIT 2 编辑2

I think there's a good case for a bug report. 我认为错误报告是个好例子。

Again more theories...(just based on reading code) See the file object source: http://svn.python.org/view/python/branches/release26-maint/Objects/fileobject.c?revision=81277&view=markup 再次更多理论......(仅基于阅读代码)查看文件对象源: http//svn.python.org/view/python/branches/release26-maint/Objects/fileobject.c?revision=81277&view=markup

file_read calls Py_UniversalNewlineFread(). file_read调用Py_UniversalNewlineFread()。 fread can return with an error with errno = EINTR (it performs its own signal handling). fread可以使用errno = EINTR(它执行自己的信号处理)返回错误。 In this case Py_UniversalNewlineFread() bails but does not perform any signal checking with PyErr_CheckSignals() so that the handlers can get called synchronously. 在这种情况下,Py_UniversalNewlineFread()保释但不会使用PyErr_CheckSignals()执行任何信号检查,以便可以同步调用处理程序。 file_read clears the file error but also does not call PyErr_CheckSignals(). file_read清除文件错误,但也不调用PyErr_CheckSignals()。

See getline() and getline_via_fgets() for examples of how it's used. 有关如何使用它的示例,请参阅getline()和getline_via_fgets()。 The pattern is documented in this bug report for a similar issue: ( http://bugs.python.org/issue1195 ). 该错误报告中记录了类似问题的模式:( http://bugs.python.org/issue1195 )。 So it seems that the signal is handled at an indeterminate time by the interpreter. 所以似乎解释器在不确定的时间处理信号。

I guess there's little value in diving any deeper since it's still not clear whether the sys.stdin.read() example is a proper analog of your "dostuff()" function. 我想深入潜水的价值不大,因为仍然不清楚sys.stdin.read()示例是否是你的“dostuff()”函数的正确模拟。 (there could be multiple bugs at play) (可能会有多个错误)

Having similar problem and this is my workaround: 有类似的问题,这是我的解决方法:

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()

sys.stdin.read() is a system call and so the behavior is going to be different for each system. sys.stdin.read()是一个系统调用,因此每个系统的行为都不同。 For windows 7 I think what is happening is that the input is being buffered and so you're getting where sys.stdin.read() is returning everything up to the Ctrl-C and as soon as you access sys.stdin again it'll send the "Ctrl-C". 对于Windows 7,我认为正在发生的事情是输入正在被缓冲,所以你得到的地方是sys.stdin.read()将所有东西都返回到Ctrl-C,并且一旦你再次访问sys.stdin它就会' ll发送“Ctrl-C”。

try the following, 试试以下,

def foo():
    try:
        print sys.stdin.read()
        print sys.stdin.closed
    except KeyboardInterrupt:
        print "Interrupted!"

This suggests that there is a buffering of stdin going on that is causing another call to stdin to recognize the keyboard input 这表明stdin正在缓冲,导致另一个stdin调用识别键盘输入

def foo():
    try:
        x=0
        while 1:
            x += 1
        print x
    except KeyboardInterrupt:
        print "Interrupted!"

there doesn't appear to be a problem. 似乎没有问题。

Is dostuff() reading from stdin? dostuff()是从stdin读取的吗?

def foo():
    try:
        x=0
        while 1:
            x+=1
            print (x)
    except KeyboardInterrupt:
       print ("interrupted!!")
foo()

That works fine. 这很好。

Here's a guess about what's happening: 这是对正在发生的事情的猜测:

  • pressing Ctrl-C breaks the "print" statement (for whatever reason... bug? Win32 limitation?) 按Ctrl-C打破“打印”语句(无论出于何种原因...... bug?Win32限制?)
  • pressing Ctrl-C also throws the first KeyboardInterrupt, in dostuff() 按下Ctrl-C也会在dostuff()中抛出第一个KeyboardInterrupt
  • The exception handler runs and tries to print "Interrupted", but the "print" statement is broken and throws another KeyboardInterrupt. 异常处理程序运行并尝试打印“Interrupted”,但“print”语句被破坏并抛出另一个KeyboardInterrupt。
  • The finally clause runs and tries to print "cleaning up....", but the "print" statement is broken and throws yet another KeyboardInterrupt. finally子句运行并尝试打印“清理....”,但“print”语句被破坏并抛出另一个KeyboardInterrupt。

This works for me: 这对我有用:

import sys

if __name__ == "__main__":
    try:
        sys.stdin.read()
        print "Here"
    except KeyboardInterrupt:
        print "Worked"
    except:
        print "Something else"
    finally:
        print "Finally"

Try putting a line outside of your dostuff() function or move the loop condition outside of the function. 尝试在dostuff()函数外部放置一条线或将循环条件移到函数外部。 For example: 例如:

try:
    while True:
        dostuff()
except KeyboardInterrupt:
    print "Interrupted!"
except:
    print "Some other exception?"
finally:
    print "cleaning up...."
    print "done."

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我不能在raw_input期间捕获KeyboardInterrupt? - Why can't I catch KeyboardInterrupt during raw_input? Python - 无法使用KeyboardInterrupt杀死主线程 - Python - Can't kill main thread with KeyboardInterrupt 无法关闭 KeyboardInterrupt 上的套接字连接(Python) - can't close socket connection on KeyboardInterrupt(Python) 为什么 KeyboardInterrupt 在 python 中不起作用? - Why is KeyboardInterrupt not working in python? Python 2.X:为什么我不能正确处理Unicode? - Python 2.X: Why Can't I Properly Handle Unicode? 在Python中,我可以阻止函数捕获KeyboardInterrupt和SystemExit吗? - In Python, can I prevent a function from catching KeyboardInterrupt and SystemExit? 无法在 KeyboardInterrupt 上关闭套接字 - can't close socket on KeyboardInterrupt Selenium 不会在 KeyboardInterrupt (Python) 上退出 - Selenium doesn't quit on KeyboardInterrupt (Python) 使用Wing IDE 101时,为什么KeyboardInterrupt不能在Python中工作? - Why won't KeyboardInterrupt work in Python when using Wing IDE 101? 为什么我无法在 VS Code 的外部终端中捕获 ctrl+C 键(python 的 KeyBoardInterrupt 异常)? - Why cannot I capture the ctrl+C key (KeyBoardInterrupt exception of python) in external terminal of VS Code?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM