简体   繁体   English

在没有 try-except 的情况下在 Python 中捕获键盘中断

[英]Capture keyboardinterrupt in Python without try-except

Is there some way in Python to capture KeyboardInterrupt event without putting all the code inside a try - except statement? Python 中是否有某种方法可以在不将所有代码放入try - except语句中的情况下捕获KeyboardInterrupt事件?

I want to cleanly exit without trace if user presses Ctrl + C .如果用户按下Ctrl + C ,我想干净地退出而不留痕迹。

Yes, you can install an interrupt handler using the module signal , and wait forever using a threading.Event :是的,您可以使用模块signal安装中断处理程序,并使用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()

If all you want is to not show the traceback, make your code like this:如果您只想不显示回溯,请使您的代码如下:

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


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

(Yes, I know that this doesn't directly answer the question, but it's not really clear why needing a try/except block is objectionable -- maybe this makes it less annoying to the OP) (是的,我知道这并没有直接回答问题,但不清楚为什么需要 try/except 块是令人反感的——也许这会让 OP 不那么烦人)

An alternative to setting your own signal handler is to use a context-manager to catch the exception and ignore it:设置自己的信号处理程序的另一种方法是使用上下文管理器来捕获异常并忽略它:

>>> 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
... 
>>>

This removes the try - except block while preserving some explicit mention of what is going on.这删除了try - except块,同时保留了对正在发生的事情的一些明确提及。

This also allows you to ignore the interrupt only in some portions of your code without having to set and reset again the signal handlers everytime.这也允许您仅在代码的某些部分忽略中断,而不必每次都再次设置和重置信号处理程序。

I know this is an old question but I came here first and then discovered the atexit module.我知道这是一个老问题,但我首先来到这里,然后发现了atexit模块。 I do not know about its cross-platform track record or a full list of caveats yet, but so far it is exactly what I was looking for in trying to handle post- KeyboardInterrupt cleanup on Linux.我不知道它的跨平台跟踪记录或完整的警告列表,但到目前为止,它正是我在 Linux 上尝试处理KeyboardInterrupt中断后清理时所寻找的。 Just wanted to throw in another way of approaching the problem.只是想以另一种方式解决问题。

I want to do post-exit clean-up in the context of Fabric operations, so wrapping everything in try / except wasn't an option for me either.我想在 Fabric 操作的上下文中进行退出后清理,因此将所有内容都包含在try / except中也不是我的选择。 I feel like atexit may be a good fit in such a situation, where your code is not at the top level of control flow.我觉得atexit可能非常适合这种情况,即您的代码不在控制流的顶层。

atexit is very capable and readable out of the box, for example: atexit功能强大且开箱即用,例如:

import atexit

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

atexit.register(goodbye)

You can also use it as a decorator (as of 2.6; this example is from the docs):您还可以将其用作装饰器(从 2.6 开始;此示例来自文档):

import atexit

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

If you wanted to make it specific to KeyboardInterrupt only, another person's answer to this question is probably better.如果您只想使其特定于KeyboardInterrupt ,那么另一个人对这个问题的回答可能会更好。

But note that the atexit module is only ~70 lines of code and it would not be hard to create a similar version that treats exceptions differently, for example passing the exceptions as arguments to the callback functions.但请注意, atexit模块只有约 70 行代码,创建一个类似的版本以不同方式处理异常并不难,例如将异常作为参数传递给回调函数。 (The limitation of atexit that would warrant a modified version: currently I can't conceive of a way for the exit-callback-functions to know about the exceptions; the atexit handler catches the exception, calls your callback(s), then re-raises that exception. But you could do this differently.) (需要修改版本的atexit的限制:目前我无法设想退出回调函数了解异常的方法; atexit处理程序捕获异常,调用您的回调,然后重新- 引发该异常。但您可以以不同的方式执行此操作。)

For more info see:有关更多信息,请参阅:

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 .您可以通过替换sys.excepthook来防止为KeyboardInterrupt打印堆栈跟踪,而无需try: ... except KeyboardInterrupt: pass (最明显且可能是“最佳”的解决方案,但您已经知道它并要求其他东西)。 Something like就像是

def custom_excepthook(type, value, traceback):
    if type is KeyboardInterrupt:
        return # do nothing
    else:
        sys.__excepthook__(type, value, traceback)

I tried the suggested solutions by everyone, but I had to improvise code myself to actually make it work.我尝试了每个人建议的解决方案,但我必须自己即兴编写代码才能使其真正发挥作用。 Following is my improvised code:以下是我的即兴代码:

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

If someone is in search for a quick minimal solution,如果有人正在寻找快速的最小解决方案,

import signal

# The code which crashes program on interruption

signal.signal(signal.SIGINT, call_this_function_if_interrupted)

# The code skipped if interrupted

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM