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

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

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!' 

这是情况。 当脚本运行时,它等待input,并且用户应该按下Ctrl + C来引发一个KeyboardInterrupt ,它应该被bing()except块捕获。 所以,这应该是实际产出。 而且,当我在Ubuntuterminal和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 (这也不pipe用),然后事情变得更多比以前复杂。 所以,我回到我的好老try... catch 。 然后,我去了Pythonist的房间。

@poke提示我们按Ctrl + C时会EOFError 。 然后, @ZeroPiraeus说 ,当按Ctrl + ZEnterEOFError

这是有帮助的, 几分钟后摆弄讨论就开始讨论了 。 很快,一切都变得混乱了! 一些结果是好的,有些是意想不到的,还有一些出了问题!

怪人!

结论是停止使用Windows,并要求我的朋友使用terminal(我同意)。 我可以通过捕捉EOFErrorKeyboardInterrupt解决这个问题。 虽然每次按Ctrl + ZEnter都觉得很懒,但这对我来说并不是什么大问题。 但是,这是对我的痴迷。

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

Whaa ???

底部没有任何东西。 那么,这里发生了什么? 为什么KeyboardInterrupt传播? 有没有什么办法可以使输出与terminal保持一致?


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

Solutions Collecting From Web of "在命令提示符下无法捕获KeyboardInterrupt两次?"

问题user2357112链接,以某种方式解释这个: 为什么我不能在Python中处理KeyboardInterrupt?

键盘中断是异步引发的,所以不会立即终止应用程序。 相反, Ctrl + C是在某种事件循环中处理的,需要一段时间才能到达那里。 这不幸意味着你不能在这种情况下可靠地捕捉KeyboardInterrupt 但是我们可以做一些事情到达那里。

正如我昨天所解释的,停止raw_input调用的异常不是KeyboardInterrupt而是一个EOFError 你可以通过改变你的bing功能来轻松验证这个:

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

您将看到打印的异常类型是EOFError而不是KeyboardInterrupt 你也会看到,这个print甚至没有完全通过:没有新的路线。 这显然是因为输出被打印语句写入异常类型到stdout之后到达的中断所中断。 你可以看到这一点,当你添加更多的东西打印:

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

请注意,我在这里为打印语句使用了两个单独的参数。 当我们执行这个时,我们可以看到“Exception raised”文本,但是“EOF Error”不会出现。 相反,从外部调用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() 之后的print也被执行了。 更糟糕的是,在某些cmd.exe的执行过程中,我得到的结果是根本没有发现键盘中断(显然,程序已经完成 ,中断才得到处理)。

那么如果我们要确保我们得到键盘中断,我们可以做些什么呢? 我们知道的一件事是,中断input() (或raw_input() )提示总是会引发一个EOFError :这是我们一直看到的一个一致的东西。 所以我们可以做的只是抓住这一点,然后确保我们得到键盘中断。

一种方法是从EOFError异常处理程序中EOFError KeyboardInterrupt 但是,这不仅感觉有点脏,而且也不能保证中断实际上是什么终止了输入提示(谁知道还有什么可能会引发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') 

现在,我们只抓住EOFError,稍等一会,让后面的异步进程安定下来,决定是否中断执行。 这一贯地允许我在外部try / catch中捕获KeyboardInterrupt ,除了我在异常处理程序中做什么以外,不会打印任何其他内容。

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

所以我们有我们的解决方案:只要抓住EOFError,然后等一下。 至少我希望这是一个解决方案,可以在其他机器上运行,而不是我自己的^ _ ^“昨天晚上,我不太确定这一点 – 但至少我对所有终端和不同的Python版本都有一致的体验。