112

Is there some way in Python to capture KeyboardInterrupt event without putting all the code inside a try-except statement?

I want to cleanly exit without trace if user presses Ctrl+C.

4

6 回答 6

160

Yes, you can install an interrupt handler using the module signal, and wait forever using a threading.Event:

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()
于 2010-11-17T14:30:36.523 回答
40

如果您只想不显示回溯,请将您的代码设为:

## all your app logic here
def main():
   ## whatever your app does.


if __name__ == "__main__":
   try:
      main()
   except KeyboardInterrupt:
      # do nothing here
      pass

(是的,我知道这并不能直接回答这个问题,但还不清楚为什么需要一个 try/except 块是令人反感的——也许这会让 OP 不那么烦人)

于 2010-11-17T14:32:56.443 回答
32

设置自己的信号处理程序的另一种方法是使用上下文管理器来捕获异常并忽略它:

>>> 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块,同时保留对正在发生的事情的一些明确提及。

这也允许您仅在代码的某些部分中忽略中断,而不必每次都重新设置和重置信号处理程序。

于 2010-11-17T15:18:34.103 回答
8

我知道这是一个老问题,但我先来到这里,然后发现了这个atexit模块。我还不知道它的跨平台跟踪记录或警告的完整列表,但到目前为止,它正是我在尝试处理KeyboardInterruptLinux 上的后清理时所寻找的。只是想以另一种方式解决问题。

我想在 Fabric 操作的上下文中进行退出后清理,因此将所有内容都包装在try/except中也不是我的选择。我觉得atexit可能很适合这种情况,您的代码不在控制流的顶层。

atexit开箱即用,功能强大且可读性强,例如:

import atexit

def goodbye():
    print "You are now leaving the Python sector."

atexit.register(goodbye)

您也可以将其用作装饰器(从 2.6 开始;此示例来自文档):

import atexit

@atexit.register
def goodbye():
    print "You are now leaving the Python sector."

如果你只想让它具体到KeyboardInterrupt,另一个人对这个问题的回答可能会更好。

但请注意,该atexit模块只有大约 70 行代码,并且创建一个类似的版本来区别对待异常并不难,例如将异常作为参数传递给回调函数。(其限制atexit将保证修改版本:目前我无法设想退出回调函数了解异常的方法;atexit处理程序捕获异常,调用您的回调,然后重新引发那个例外。但你可以用不同的方式来做。)

有关更多信息,请参阅:

于 2013-11-05T14:15:54.377 回答
4

You can prevent printing a stack trace for KeyboardInterrupt, without try: ... except KeyboardInterrupt: pass (the most obvious and propably "best" solution, but you already know it and asked for something else) by replacing sys.excepthook. Something like

def custom_excepthook(type, value, traceback):
    if type is KeyboardInterrupt:
        return # do nothing
    else:
        sys.__excepthook__(type, value, traceback)
于 2010-11-17T14:27:39.287 回答
3

我尝试了每个人建议的解决方案,但我必须自己即兴编写代码才能使其真正起作用。以下是我的即兴代码:

import signal
import sys
import time

def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    print(signal) # Value is 2 for CTRL + C
    print(frame) # Where your execution of program is at moment - the Line Number
    sys.exit(0)

#Assign Handler Function
signal.signal(signal.SIGINT, signal_handler)

# Simple Time Loop of 5 Seconds
secondsCount = 5
print('Press Ctrl+C in next '+str(secondsCount))
timeLoopRun = True 
while timeLoopRun:  
    time.sleep(1)
    if secondsCount < 1:
        timeLoopRun = False
    print('Closing in '+ str(secondsCount)+ ' seconds')
    secondsCount = secondsCount - 1
于 2020-05-14T20:11:41.820 回答