Capture keyboardinterrupt in Python without try-except
在python中,是否有某种方法可以捕获KeyboardInterrupt事件而不将所有代码放在try到except语句中?
如果用户按ctrl+c,我希望完全退出而不进行跟踪。
是的,您可以使用模块信号安装中断处理程序,并使用线程永久等待。事件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import signal
import sys
import time
import threading
def signal_handler(signal, frame):
print('You pressed Ctrl+C!')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
forever = threading.Event()
forever.wait() |
号
- 请注意,信号模块存在一些特定于平台的问题——不应影响此海报,但"在Windows上,信号()只能用sigabrt、sigfpe、sigill、sigint、sigsegv或sigterm调用。在任何其他情况下都将引发ValueError。"
- 也适用于线程。不过,我希望你永远不要这样做。(以这种方式,不管怎样,while True: pass会更整洁。)这是非常浪费的;尝试类似while True: time.sleep(60 * 60 * 24)的方法(一次睡一天完全是随意的)。
- 如果你使用的是Chris Morgan关于使用time的建议(你应该这样做),不要忘记使用import time。
- 调用sys.exit(0)会为我触发SystemExit异常。如果将其与以下内容结合使用,您可以使其工作得很好:stackoverflow.com/a/13723190/353094
- 您可以使用signal.pause()而不是重复睡眠
- 如果使用多处理,这不会完全终止程序。
- 这对我一点作用都没有。
- 另请参阅:stackoverflow.com/a/37446275/1959808
如果您只想不显示回溯,请将代码设置为如下:
1 2 3 4 5 6 7 8 9 10 11
| ## all your app logic here
def main():
## whatever your app does.
if __name__ =="__main__":
try:
main()
except KeyboardInterrupt:
# do nothing here
pass |
(是的,我知道这并不能直接回答这个问题,但不清楚为什么需要一个try/except块是令人讨厌的——也许这会使操作不那么烦人)
- 出于某种原因,这对我来说并不总是有效的。signal.signal( signal.SIGINT, lambda s, f : sys.exit(0))总是这样。
- 这并不总是适用于使用线程的PyGTK之类的东西。有时^C只会终止当前线程而不是整个进程,因此异常只会在该线程中传播。
- 还有一个关于ctrl+c和pygtk的问题:stackoverflow.com/question s/16410852/…
设置自己的信号处理程序的另一种方法是使用上下文管理器捕获异常并忽略它:
1 2 3 4 5 6 7 8 9 10 11 12
| >>> class CleanExit(object):
... def __enter__(self):
... return self
... def __exit__(self, exc_type, exc_value, exc_tb):
... if exc_type is KeyboardInterrupt:
... return True
... return exc_type is None
...
>>> with CleanExit():
... input() #just to test it
...
>>> |
。
这删除了try--except块,同时保留了一些关于正在发生的事情的明确说明。
这还允许您只忽略代码的某些部分的中断,而不必每次都设置和重置信号处理程序。
- 很好,这个解决方案在表达目的上似乎比处理信号更直接一些。
- 使用多处理库,我不确定应该在哪个对象上添加这些方法。有什么线索吗?
- @圣潘,你什么意思?在处理多处理时,您必须同时处理父进程和子进程中的信号,因为这两个进程都可能触发信号。这真的取决于你在做什么以及你的软件将如何使用。
我知道这是一个古老的问题,但我首先来到这里,然后发现了atexit模块。我还不知道它的跨平台跟踪记录或完整的警告列表,但到目前为止,这正是我在尝试处理Linux上的后KeyboardInterrupt清理时所寻找的。只是想用另一种方式来解决这个问题。
我想在织物操作的背景下进行出口后清理,所以用try或except包装所有东西也不是我的选择。我觉得atexit可能很适合这种情况,在这种情况下,您的代码不在控制流的顶层。
atexit是一种非常有能力和可读性的开箱即用产品,例如:
1 2 3 4 5 6
| import atexit
def goodbye():
print"You are now leaving the Python sector."
atexit.register(goodbye) |
您也可以将其用作修饰器(从2.6开始;此示例来自文档):
1 2 3 4 5
| import atexit
@atexit.register
def goodbye():
print"You are now leaving the Python sector." |
。
如果你只想把它具体化到KeyboardInterrupt,那么另一个人对这个问题的回答可能会更好。
但是请注意,atexit模块只有大约70行代码,因此不难创建一个处理异常不同的类似版本,例如将异常作为参数传递给回调函数。(需要修改版本的atexit的限制:目前我想不出一种方法让exit回调函数知道异常;atexit处理程序捕获异常,调用回调,然后重新引发该异常。但你可以用不同的方法来做。)
有关详细信息,请参阅:
- 关于atexit的正式文件
- 周帖的python模块,很好的介绍
- ATEXIT不适用于键盘中断(python 3.7)
如果没有try: ... except KeyboardInterrupt: pass(最明显和最好的"最佳"解决方案,但您已经知道并要求其他解决方案),可以通过替换sys.excepthook来防止打印KeyboardInterrupt的堆栈跟踪。有点像
1 2 3 4 5
| def custom_excepthook(type, value, traceback):
if type is KeyboardInterrupt:
return # do nothing
else:
sys.__excepthook__(type, value, traceback) |
- 如果用户按ctrl-c,我希望不带跟踪的干净退出
- 捕获=>捕获
- 这根本不是真的。键盘中断异常是在中断处理程序期间创建的。sigint的默认处理程序会引发keyboardinterrupt,因此如果您不希望出现这种行为,那么您所要做的就是为sigint提供一个不同的信号处理程序。您的是正确的,因为异常只能在Try/Exception中处理,但是在这种情况下,您可以首先阻止引发异常。
- 是的,我在发帖三分钟后,当科特林斯基的回答传来时,我就知道了这一点;)