Mau*_*kye 6 python exception input interrupt python-3.x
我正在使用python创建一个非常基本的命令行实用程序,并且试图将其设置为使用户能够在执行Ctrl + C的过程中中断任何正在运行的操作,并通过发送EOF并退出程序来退出程序Ctrl + Z。但是,在最后一天,我一直在解决这个令人沮丧的问题,在取消使用a的运行操作后,再次KeyboardInterrupt按Ctrl + C会发送EOFError而不是a KeyboardInterrupt,这将导致程序退出。KeyboardInterrupt除了在我输入任何命令或空行(在其中发送附加命令KeyboardInterrupt而不是我提供的输入)之前,随后按Ctrl + C会一直发送。这样做之后,再次按Ctrl + C将再次发送EOFError,然后从那里继续。这是演示我的问题的代码的最小示例;
import time
def parse(inp):
time.sleep(1)
print(inp)
if inp == 'e':
return 'exit'
while True:
try:
user_in = input("> ").strip()
except (KeyboardInterrupt, Exception) as e:
print(e.__class__.__name__)
continue
if not user_in:
continue
try:
if parse(user_in) == 'exit':
break
except KeyboardInterrupt:
print("Cancelled")
Run Code Online (Sandbox Code Playgroud)
这是我的代码的一些示例输出;
>
>
>
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
>
>
> ^Z
EOFError
> ^Z
EOFError
> ^Z
EOFError
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
>
> ^Z
EOFError
> KeyboardInterrupt
>
>
Run Code Online (Sandbox Code Playgroud)
如您所见,当按Ctrl + C,Ctrl + Z或空白行时,提示符将按照您期望的那样正确响应每个错误。但是,如果我运行命令并尝试在执行过程中通过按Ctrl + C取消它;
> test
Cancelled
>
>
>
> EOFError
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
KeyboardInterrupt
>
>
> EOFError
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
KeyboardInterrupt
>
>
>
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,我只按了Ctrl + C,然后按Enter。我先按Enter键发送几个空行,然后按Ctrl + C,首先发送一个EOFError。之后按Ctrl + C多个时间,然后KeyboardInterrupt正确发送。之后,先发送空白行,然后发送KeyboardInterrupt,然后正常接收随后输入的空白行。从程序的执行开始重复此功能。
为什么会发生这种情况,我该如何解决?
所以。您发现了一个相当古老的 Python bug。这与键盘中断的异步性质有关,以及如果您KeyboardInterrupt在挂起时向 Python 发送 a ,并且它不响应中断,第二个中断将引发更强的EOFError。然而,这两者似乎发生了冲突,如果你有一个异步KeyboardInterrupt捕获,然后是第二个输入KeyboardInterrupt,那么某些缓冲区中会留下一些东西,从而触发了EOFError。
我知道这不是一个很好的解释,也不是很清楚。然而,它允许一个非常简单的修复。让缓冲区赶上所有异步中断,然后开始等待输入:
import time
def parse(inp):
time.sleep(1)
print(inp)
if inp == 'e':
return 'exit'
while True:
try:
user_in = input("> ").strip()
except (KeyboardInterrupt, Exception) as e:
print(e.__class__.__name__)
continue
if not user_in:
continue
try:
if parse(user_in) == 'exit':
break
except KeyboardInterrupt:
print("Cancelled")
time.sleep(0.1) # This is the only line that's added
Run Code Online (Sandbox Code Playgroud)
现在执行与您相同的操作会产生以下结果:
> test
Cancelled
>
>
>
>
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
> KeyboardInterrupt
>
>
> KeyboardInterrupt
> KeyboardInterrupt
>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
63 次 |
| 最近记录: |