Ctrl+C 取消进程后发送一次 EOFError
Ctrl+C sends EOFError once after cancelling process
我正在使用 python 制作一个非常基本的命令行实用程序,我正在尝试对其进行设置,以便用户能够在执行期间中断任何 运行ning 操作使用 Ctrl+C,然后使用 Ctrl+Z 发送 EOF 退出程序。然而,在最后一天我一直在与这个令人沮丧的问题作斗争,在用 KeyboardInterrupt
取消 运行ning 操作后,第二次按 Ctrl+C 发送 EOFError
而不是a KeyboardInterrupt
,导致程序退出。随后按 Ctrl+C 会像往常一样发送 KeyboardInterrupt
,除非我输入任何命令或空行,其中会发送额外的 KeyboardInterrupt
而不是我提供的输入。这样做之后,再次按 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")
这是我的代码的一些示例输出;
>
>
>
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
>
>
> ^Z
EOFError
> ^Z
EOFError
> ^Z
EOFError
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
>
> ^Z
EOFError
> KeyboardInterrupt
>
>
如您所见,当按下 Ctrl+C、Ctrl+Z 或空白行时,提示会按照您预期的方式正确响应每个错误。但是,如果我 运行 一个命令并尝试在执行过程中通过按 Ctrl+C 来取消它;
> test
Cancelled
>
>
>
> EOFError
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
KeyboardInterrupt
>
>
> EOFError
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
KeyboardInterrupt
>
>
>
我在上面的例子中只按了Ctrl+C和Enter;我先按回车键发送几个空行,然后按 Ctrl+C,它首先发送一个 EOFError
。之后多次按 Ctrl+C,然后正确发送 KeyboardInterrupt
。之后,发送一个空行,而不是发送一个KeyboardInterrupt
,随后输入的空行被正常接收。从那里的程序执行开始重复此功能。
为什么会发生这种情况,我该如何解决?
所以。您找到了一个相当古老的 Python bug。这与键盘中断的异步性质有关,以及如果您在挂起时向 Python 发送 KeyboardInterrupt
,并且它不响应中断,那么第二个中断将引发更强的 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
现在执行与您相同的操作会产生以下结果:
> test
Cancelled
>
>
>
>
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
> KeyboardInterrupt
>
>
> KeyboardInterrupt
> KeyboardInterrupt
>
我正在使用 python 制作一个非常基本的命令行实用程序,我正在尝试对其进行设置,以便用户能够在执行期间中断任何 运行ning 操作使用 Ctrl+C,然后使用 Ctrl+Z 发送 EOF 退出程序。然而,在最后一天我一直在与这个令人沮丧的问题作斗争,在用 KeyboardInterrupt
取消 运行ning 操作后,第二次按 Ctrl+C 发送 EOFError
而不是a KeyboardInterrupt
,导致程序退出。随后按 Ctrl+C 会像往常一样发送 KeyboardInterrupt
,除非我输入任何命令或空行,其中会发送额外的 KeyboardInterrupt
而不是我提供的输入。这样做之后,再次按 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")
这是我的代码的一些示例输出;
>
>
>
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
>
>
> ^Z
EOFError
> ^Z
EOFError
> ^Z
EOFError
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
>
> ^Z
EOFError
> KeyboardInterrupt
>
>
如您所见,当按下 Ctrl+C、Ctrl+Z 或空白行时,提示会按照您预期的方式正确响应每个错误。但是,如果我 运行 一个命令并尝试在执行过程中通过按 Ctrl+C 来取消它;
> test
Cancelled
>
>
>
> EOFError
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
KeyboardInterrupt
>
>
> EOFError
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
KeyboardInterrupt
>
>
>
我在上面的例子中只按了Ctrl+C和Enter;我先按回车键发送几个空行,然后按 Ctrl+C,它首先发送一个 EOFError
。之后多次按 Ctrl+C,然后正确发送 KeyboardInterrupt
。之后,发送一个空行,而不是发送一个KeyboardInterrupt
,随后输入的空行被正常接收。从那里的程序执行开始重复此功能。
为什么会发生这种情况,我该如何解决?
所以。您找到了一个相当古老的 Python bug。这与键盘中断的异步性质有关,以及如果您在挂起时向 Python 发送 KeyboardInterrupt
,并且它不响应中断,那么第二个中断将引发更强的 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
现在执行与您相同的操作会产生以下结果:
> test
Cancelled
>
>
>
>
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
> KeyboardInterrupt
>
> KeyboardInterrupt
>
>
> KeyboardInterrupt
> KeyboardInterrupt
>