在命令提示符中不能捕获KeyboardInterrupt两次? [英] Cannot catch KeyboardInterrupt in command prompt twice?

查看:2075
本文介绍了在命令提示符中不能捕获KeyboardInterrupt两次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

今天,我不得不检查我的脚本在Windows命令提示符 [1] 上的运行情况,当我注意到一些奇怪的东西。我在做类似的事情,但这足以证明这个问题。这是代码。

  def bing():
try:
raw_input()
除了KeyboardInterrupt:
print'这是什么实际发生在这里!

尝试:#pardon我那些奇怪的字符串
bing()#,因为它与聊天中的一切一致房间(见下文)
print'Yoo hoo ...'
except KeyboardInterrupt:
print'这里也没有什么事情发生了!'

这是情况。脚本运行时,它会等待输入,用户应按 Ctrl + C 以生成 KeyboardInterrupt bing()中的所以,这应该是实际的输出。而且,这是当我在Ubuntu终端和IDLE(在Windows和Ubuntu上)运行时会发生什么。

 是什么实际发生在这里! 
Yoo hoo ...

但是,这不符合Windows命令提示符。我真的得到了一个奇怪的输出。

 这是发生在这里!这里也没有什么发生! 

看起来像一个 KeyboardInterrupt 程序并终止它。



我试过了我能做的一切。首先,我使用 signal.signal 来处理 SIGINT (没有工作),然后我使用处理函数来产生一个异常,我后来捕获它(也没有工作),然后事情变得比以前更复杂。所以,我回到我的好老 try ... catch 。然后,我去了Python的房间。



链接的问题,解释此在某些方面:为什么我不能处理在Python中的KeyboardInterrupt? / a>。



键盘中断是异步引发的,因此不会立即终止应用程序。相反, 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():

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

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



失去控制在Python 3虽然。代码如下:

  def bing():
try:
input除了异常为e:
print('Exception raised:',type(e))

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

很多正是我们以前做的,只是修改为Python 3语法。如果我运行这个,我得到以下输出:

 引发异常:< class'EOFError'> 
After bing
Final KeyboardInterrupt

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



那么,如果我们想确保我们得到一个键盘中断,我们能做些什么呢?我们知道的一件事是确保中断 input()(或 raw_input())提示 always 引发 EOFError :这是我们一直见过的一致的事情。所以我们可以做的只是捕捉,然后确保我们得到键盘中断。



一个办法是只是提出一个 KeyboardInterrupt EOFError 的异常处理程序。但这不仅感觉有点肮脏,它也不保证中断实际上是什么终止输入提示首先(谁知道什么可能提出EOFError?)。所以我们应该已经存在的中断信号产生异常。



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

 导入时间
除了EOFError:
time.sleep(1)





try(
try:
输入()#或raw_input b try:
bing()
print('After bing')
except KeyboardInterrupt:
print('Final KeyboardInterrupt')

现在,我们只需要捕获EOFError并等待一段时间,让异步进程在后台解决并决定是否中断执行。这始终允许我捕获外部try / catch中的 KeyboardInterrupt ,并且不会打印除了在异常处理程序中执行的任何操作。



你可能担心一秒钟是等待很长时间,但在我们的情况下,我们中断执行,第二个从来没有真正持续很长时间。在 time.sleep 之后的几毫秒,中断被捕获,我们在我们的异常处理程序中。因此,一秒钟只是一个失效保护,它将等待足够长的时间,以确保异常 及时到达。在最坏的情况下,当实际上没有中断,但只是一个正常EOFError?然后,以前为用户输入无限阻塞的程序将需要更长的时间才能继续;这不应该真的是一个问题(更不要说EOFError可能是非常罕见的)。



所以我们有我们的解决方案:只需要抓住EOFError,稍等一会。至少我希望这是一个解决方案,在其他机器上工作,而不是我自己的^ _ ^昨晚后,我不太确定这一点 - 但至少我有一个一致的经验,所有的终端和不同的Python版本。


Today, I had to check how my script runs on the Windows command prompt[1], when I noticed something weird. I was working on something similar to this, but this is enough to demonstrate the problem. Here's the code.

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

Here's the situation. When the script runs, it waits for the input and the user is supposed to press Ctrl+C to raise a KeyboardInterrupt which would (should) be caught by the except block within bing(). So, this should be the actual output. And, this is what happens when I run it in my Ubuntu terminal and IDLE (on both Windows & Ubuntu).

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

But, this doesn't go as expected on the Windows command prompt. I rather get a strange output.

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

It looks like a single KeyboardInterrupt propagates throughout the program and finally terminates it.

I tried everything I could do. First, I used a signal.signal to handle the SIGINT (which didn't work), and then I used handling function to raise an Exception which I'd later catch (which didn't work either), and then things got more complicated than it used to be. So, I landed back to my good old try... catch. Then, I went to the room for Pythonists.

@poke suggested that an EOFError is raised when we press Ctrl+C. Then, @ZeroPiraeus said that EOFError is raised when one presses Ctrl+Z and Enter.

That was helpful, which drove the discussion after a few minutes of fiddling around. Soon, everything became chaos! Some results were good, some were unexpected, and a few went haywire!

The conclusion was to stop using Windows and ask my friends to use the Terminal (I agree). I could however do a workaround by catching the EOFError along with the KeyboardInterrupt. While it feels lazy to press Ctrl+Z and Enter each time, that's not a big problem for me. But, this is an obsession for me.

On further research, I also noticed that there's no KeyboardInterrupt raised on the CMD when I press Ctrl+C.

There's nothing at the bottom. So, what happens here anyway? Why does the KeyboardInterrupt propagate? Is there any way (at all) to make the output consistent with the terminal?


[1]: I've always worked on the terminal, but today I needed to ensure that my script works on all platforms (especially because most of my friends are non-coders and just stick to Windows).

解决方案

The question user2357112 linked, explains this in some way: Why can't I handle a KeyboardInterrupt in python?.

The keyboard interrupt is raised asynchronously, so it does not immediately terminate the application. Instead, the Ctrl+C is handled in some kind of event loop that takes a while to get there. This unfortunately means that you cannot reliably catch the KeyboardInterrupt in this case. But we can do some things to get there.

As I explained yesterday, the exception that stops the raw_input call is not the KeyboardInterrupt but an EOFError. You can easily verify this by changing your bing function like this:

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

You will see that the exception type that’s printed is EOFError and not KeyboardInterrupt. You will also see that the print did not even go through completely: There is no new line. That’s apparently because the output got interrupted by the interrupt which arrived just after the print statement wrote the exception type to stdout. You can see this also when you add a bit more stuff to the print:

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

Note that I’m using two separate arguments here for the print statement. When we execute this, we can see the "Exception raised" text, but the "EOF Error" won’t appear. Instead, the except from the outer call will trigger and the keyboard interrupt is caught.

Things get a bit more out of control in Python 3 though. Take this code:

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

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

This is pretty much exactly what we did before, just amended for Python 3 syntax. If I run this, I get the following output:

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

So we can again see, that the EOFError is correctly caught, but for some reason Python 3 continues the execution a lot longer than Python 2 here, as the print after bing() is executed as well. What’s worse, in some executions with cmd.exe, I get the result that no keyboard interrupt is caught at all (so apparently, the interrupt got processed after the program already completed).

So what can we do about this if we want to make sure that we get a keyboard interrupt? One thing we know for sure is that interrupting an input() (or raw_input()) prompt always raises an EOFError: That’s the one consistent thing that we have seen all the time. So what we can do is just catch that, and then make sure that we get the keyboard interrupt.

One way to do this would be to just raise a KeyboardInterrupt from the exception handler for EOFError. But this not only feels a bit dirty, it also doesn’t guarantee that an interrupt is actually what terminated the input prompt in the first place (who knows what else can possibly raise an EOFError?). So we should have the already existing interrupt signal generate the exception.

The way we do this is quite simple: We wait. So far, our problem was, that the execution continued because the exception didn’t arrive fast enough. So what if we wait a bit to let the exception eventually arrive before we continue with other things?

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

Now, we just catch the EOFError and wait a bit to let the asynchronous processes in the back settle and decide on whether to break the execution or not. This consistently allows me to catch the KeyboardInterrupt in the outer try/catch and will not print anything else except what I do in the exception handler.

You might worry that one second is a long time to wait, but in our cases, where we interrupt the execution, that second never really lasts long. Just a few milliseconds after the time.sleep, the interrupt is caught and we’re in our exception handler. So the one second is just a fail-safe that will wait long enough that the exception definitely arrives in time. And in the worst case, when there isn’t actually an interrupt but just a "normal" EOFError? Then the program that was previously blocking infinitely for user input will take a second longer to continue; that shouldn’t really be a problem ever (not to mention that the EOFError is probably super rare).

So there we have our solution: Just catch the EOFError, and wait a bit. At least I hope that this is a solution that works on other machines than my own ^_^" After last night, I’m not too sure about this—but at least I got a consistent experience over all my terminals and different Python versions.

这篇关于在命令提示符中不能捕获KeyboardInterrupt两次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆