在Python中,我能否防止一个函数捕获KeyboardInterrupt和SystemExit?

7
我是一名Python开发者,正在编写以下类似的代码:

import sys
try:
    for x in large_list:
        function_that_catches_KeyboardInterrupt(x)
except KeyboardInterrupt:
    print "Canceled!"
    sys.exit(1)

当我尝试中断循环时,我基本上需要按住 Control+C 足够长的时间,以取消对于 large-list 所有元素的函数的每次调用,只有这样我的程序才会退出。
有没有办法防止函数捕获 KeyboardInterrupt,以便我可以自己捕获它?我能想到的唯一方法是通过创建一个单独的线程来调用该函数,但那似乎有些过分。
编辑:我检查了有问题的代码(我不能轻易更改),它实际上使用了裸的 except:,因此即使是 sys.exit(1) 也被视为 SystemExit 异常。如何从裸的 except: 块中退出并退出我的程序?

对我来说似乎并不过分...而且因为我能想到的每个其他解决方案都涉及某种黑魔法... - GaretJax
请注意,每次调用不需要为其使用一个线程,只需要一个就足够了。;-) - GaretJax
1个回答

6
你可以使用signal库重新绑定SIGINT处理程序。
import signal, sys
def handler(signal, frame):
    print "Canceled!"
    sys.exit(1)
signal.signal(signal.SIGINT, handler)
for x in large_list:
    function_that_catches_KeyboardInterrupt(x)

SystemExit 被捕获时,有几种退出的方式。使用 os._exit(1) 可以进行 C 风格的退出而不需要清理。使用 os.kill(os.getpid(), signal.SIGTERM) 将允许解释器进行某种程度的清理,我认为这包括刷新/关闭文件句柄等操作。


这正是默认处理程序所做的。信号处理程序是一个回调函数,由解释器在内部调用。你理论上可以引发不同的异常,但我认为这样做没有帮助。大多数函数不会显式地捕获KeyboardInterrupt,而是通过使用通用的except:块而非惯用的except Exception:块意外地捕获它,并因此抑制您尝试抛出的任何自定义异常。 - stefan
此外,当出现 KeyboardInterrupt 时,我想要做的就是 sys.exit(1),所以我不在乎它如何被捕获,我只想捕获它。但是,在循环结束后有没有办法撤消这个操作呢? - Ryan C. Thompson
1
是的。signal.signal 返回旧的处理程序,因此您可以执行 default_sigint_handler = signal.signal(signal.SIGINT, handler) ; SOME MISBEHAVING CODE ; signal.signal(signal.SIGINT, default_sigint_handler) - stefan
好的,很棒。另一个问题是那个不正常运行的代码也会捕获SystemExit。我该如何绕过它? - Ryan C. Thompson
1
那个有点棘手,因为SystemExit已经深入到解释器中了。一般来说,我不知道有什么好的方法可以做到这一点。在这种情况下,如果您只是想真正退出,而不管代码是否出现问题,您可以调用os._exit(1)进行非常粗糙的退出,或者调用os.kill(os.getpid(), signal.SIGTERM)进行略微不太粗糙的退出(我相信这将刷新文件句柄等)。 - stefan
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接