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

Waf*_*nut 16 python windows cmd exception-handling

今天,当我注意到一些奇怪的东西时,我必须检查我的脚本如何在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!'
Run Code Online (Sandbox Code Playgroud)

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

This is what actually happened here!
Yoo hoo...
Run Code Online (Sandbox Code Playgroud)

但是,这不会在Windows命令提示符下按预期方式进行.我宁愿得到一个奇怪的输出.

This is what actually happened here! Nothing happens here too!
Run Code Online (Sandbox Code Playgroud)

它看起来像是KeyboardInterrupt在整个程序中传播并最终终止它.

我尽我所能.首先,我使用a signal.signal来处理SIGINT(它没有用),然后我使用处理函数来引发Exception我后来捕获的(它也没有工作),然后事情变得比以前更复杂了是.所以,我回到了我的好老try... catch.然后,我去了Python教室.

EOFError当我们按Ctrl+ 时,@ poke建议引发一个C.然后,@ ZeroPiraeus表示,EOFError当按下Ctrl+ Z和时,它会被提升Enter.

这很有帮助,在几分钟的摆弄之后推动了讨论.很快,一切都变得混乱!有些结果很好,有些是出乎意料的,还有一些是乱七八糟的!

怪人!

结论是停止使用Windows并要求我的朋友使用终端(我同意).然而,我可以通过EOFError跟随它来做一个解决方法KeyboardInterrupt.虽然感觉懒得按Ctrl+ ZEnter每一次,这不是对我来说是很大的问题.但是,这是对我的一种痴迷.

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

Whaa ???

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


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

pok*_*oke 8

user2357112链接的问题,以某种方式解释了这一点:为什么我不能在python中处理KeyboardInterrupt?.

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

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

def bing():
    try:
        raw_input()
    except Exception as e:
        print(type(e))
Run Code Online (Sandbox Code Playgroud)

你会看到,该集团的印刷异常类型EOFError,而不是KeyboardInterrupt.你也会看到它print甚至没有完全通过:没有新线.这显然是因为在print语句将异常类型写入stdout之后刚刚到达的中断导致输出中断.当您向打印件添加更多内容时,您也可以看到这一点:

def bing():
    try:
        raw_input()
    except EOFError as e:
        print 'Exception raised:', 'EOF Error'
Run Code Online (Sandbox Code Playgroud)

请注意,我在这里使用两个单独的参数作为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')
Run Code Online (Sandbox Code Playgroud)

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

Exception raised: <class 'EOFError'>
After bing
Final KeyboardInterrupt
Run Code Online (Sandbox Code Playgroud)

所以我们可以再次看到,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')
Run Code Online (Sandbox Code Playgroud)

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

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

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