无法在命令提示符中捕获KeyboardInterrupt两次?

时间:2015-06-30 00:26:29

标签: python windows exception-handling cmd

今天,当我注意到一些奇怪的东西时,我必须检查我的脚本如何在Windows命令提示符 [1] 上运行。我正在研究类似的东西,但这足以证明这个问题。这是代码。

def bing():
    try:
        raw_input()
    except KeyboardInterrupt:
        print 'This is what actually happened here!'

try:                     # pardon me for those weird strings
    bing()               # as it's consistent with everything in the chat room (see below)
    print 'Yoo hoo...'
except KeyboardInterrupt:
    print 'Nothing happens here too!'

这是情况。当脚本运行时,它等待输入,用户应该按 Ctrl + C 来引发一个KeyboardInterrupt,它将(应该)被捕获except中的bing()块。所以,这应该是实际的输出。而且,当我在我的Ubuntu终端和IDLE(在Windows和Ubuntu上)运行它时会发生这种情况。

This is what actually happened here!
Yoo hoo...

但是,这在Windows命令提示符下没有按预期进行。我宁愿得到一个奇怪的输出。

This is what actually happened here! Nothing happens here too!

看起来单个KeyboardInterrupt在整个程序中传播并最终终止它。

我尽我所能。首先,我使用signal.signal来处理SIGINT(这不起作用),然后我使用处理函数来引发Exception我后来捕获(没有'或者工作),然后事情变得比以前更加复杂。所以,我回到了我的老try... catch。然后,我去了Python教室。

@poke suggested当我们按 Ctrl + C 时,会引发EOFError。然后,当{kbd> Ctrl + Z 输入时,@ZeroPiraeus said EOFError被抬起。

这很有帮助,推动了discussion after a few minutes of fiddling around。很快,一切都变得混乱!一些结果很好,一些是出乎意料的,还有一些went haywire!

Weirdo!

结论是停止使用Windows并要求我的朋友使用终端(我同意)。然而,我可以通过EOFErrorKeyboardInterrupt一起来解决这个问题。虽然每次按 Ctrl + Z Enter 感觉很懒,但对我来说这不是一个大问题。但是,这是对我的一种痴迷。

在进一步的研究中,我还注意到当我按 Ctrl + C 时,CMD上没有KeyboardInterrupt

Whaa???

底部没什么。那么,这里到底发生了什么?为什么KeyboardInterrupt会传播?是否有任何方法(根本)使输出与终端一致?

[1]:我一直在终端上工作,但今天我需要确保我的脚本适用于所有平台(特别是因为我的大多数朋友都是非编码员而只是坚持使用Windows)。功能

1 个答案:

答案 0 :(得分:6)

问题user2357112相关联,以某种方式解释了这一点:Why can't I handle a KeyboardInterrupt in python?

键盘中断是异步引发的,因此它不会立即终止应用程序。相反, Ctrl + C 在某种事件循环中处理,需要一段时间才能到达那里。遗憾的是,在这种情况下,您无法可靠地捕获KeyboardInterrupt。但我们可以做些事情来实现目标。

正如我昨天解释的那样,停止raw_input通话的例外不是KeyboardInterrupt而是EOFError。您可以通过更改bing功能来轻松验证这一点:

def bing():
    try:
        raw_input()
    except Exception as e:
        print(type(e))

您将看到打印的例外类型为EOFError而非KeyboardInterrupt。您还会看到print甚至没有完全通过:没有新行。这显然是因为在print语句将异常类型写入stdout之后刚刚到达的中断导致输出中断。当您向印刷品添加更多东西时,您也可以看到这一点:

def bing():
    try:
        raw_input()
    except EOFError as e:
        print 'Exception raised:', 'EOF Error'

请注意,我在这里使用两个单独的参数作为print语句。当我们执行此操作时,我们可以看到“异常引发”文本,但不会出现“EOF错误”。相反,来自外部调用的except将触发并捕获键盘中断。

然而,在Python 3中,事情变得更加失控。拿这个代码:

def bing():
    try:
        input()
    except Exception as e:
        print('Exception raised:', type(e))

try:
    bing()
    print('After bing')
except KeyboardInterrupt:
    print('Final KeyboardInterrupt')

这正是我们之前所做的,只是针对Python 3语法进行了修改。如果我运行它,我得到以下输出:

Exception raised: <class 'EOFError'>
After bing
Final KeyboardInterrupt

所以我们可以再次看到,EOFError被正确捕获,但由于某些原因,Python 3继续执行比Python 2更长的时间,因为 bing()之后的打印是也执行了。更糟糕的是,在使用cmd.exe的一些执行中,我得到的结果是根本没有捕获到键盘中断(显然,在程序已经完成之后,中断得到了处理)。

那么如果我们想确保获得键盘中断,我们可以做些什么呢?我们确切知道的一件事是,中断input()(或raw_input())提示总是会引发EOFError:这是我们看到的一致的事情时间。所以我们能做的就是抓住它,然后确保我们得到键盘中断。

执行此操作的一种方法是从KeyboardInterrupt的异常处理程序中引发EOFError。但这不仅感觉有点脏,它也不能保证中断实际上是首先终止了输入提示(谁知道还有什么可能引发EOFError?)。所以我们应该让已经存在的中断信号产生异常。

我们这样做的方式非常简单:我们等待。到目前为止,我们的问题是,执行仍在继续,因为异常没有足够快地到达。那么如果我们等待一段时间让异常最终到达我们继续其他事情呢?

import time
def bing():
    try:
        input() # or raw_input() for Python 2
    except EOFError:
        time.sleep(1)

try:
    bing()
    print('After bing')
except KeyboardInterrupt:
    print('Final KeyboardInterrupt')

现在,我们只是捕获E​​OFError并稍等一下让后面的异步进程解决并决定是否中断执行。这始终允许我捕获外部try / catch中的KeyboardInterrupt,除了在异常处理程序中执行的操作外,不会打印任何其他内容。

你可能会担心一秒钟需要等待很长时间,但在我们的情况下,我们会中断执行,那第二次永远不会持续很长时间。在time.sleep之后的几毫秒,中断被捕获,我们在异常处理程序中。所以一秒钟只是一个故障保护,它将等待足够长的时间,以及肯定到达的异常。在最坏的情况下,当实际上没有中断而只是“正常”的EOFError时?然后,之前无限地阻止用户输入的程序将花费更长的时间来继续;这不应该是一个问题(更不用说EOFError可能是超级罕见的)。

所以我们有解决方案:抓住EOFError,等一下。至少我希望这是一个适用于其他机器的解决方案,而不是我自己的^ _ ^&#34;昨晚之后,我对此并不太确定 - 但至少我在所有终端和不同的Python版本上都获得了一致的体验。