Захватить прерывание клавиатуры в Python без try-кроме

Есть ли способ в Python для захвата KeyboardInterrupt событие, не помещая весь код внутри try-except заявление?

Я хочу чисто выйти без трассировки, если пользователь нажимает Ctrl+C.

5 ответов

Решение

Да, вы можете установить обработчик прерываний, используя сигнал модуля, и ждать вечно, используя потоки. Событие:

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()

Если все, что вам нужно, это не показывать трассировку, сделайте ваш код следующим образом:

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


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

(Да, я знаю, что это не дает прямого ответа на вопрос, но не совсем понятно, зачем нужен блок try/ Кроме того, что нежелательно - возможно, это делает его менее раздражающим для OP)

Альтернативой настройке собственного обработчика сигнала является использование менеджера контекста, чтобы перехватить исключение и проигнорировать его:

>>> 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 модуль. Я еще не знаю о его кроссплатформенном послужном списке или полном списке предостережений, но пока это именно то, что я искал, пытаясь справиться сKeyboardInterrupt очистка в Linux. Просто хотел бросить по-другому подход к проблеме.

Я хочу выполнить очистку после выхода в контексте операций 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 это потребовало бы модифицированной версии: в настоящее время я не могу придумать, как функции exit-callback могли бы узнать об исключениях; atexit Обработчик ловит исключение, вызывает ваш обратный вызов (-ы), а затем повторно вызывает это исключение. Но вы могли бы сделать это по-другому.)

Для получения дополнительной информации см.:

Я пробовал все предлагаемые решения, но мне пришлось самому импровизировать код, чтобы заставить его работать. Вот мой импровизированный код:

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

Вы можете предотвратить печать трассировки стека для KeyboardInterrupt без try: ... except KeyboardInterrupt: pass (самое очевидное и, вероятно, "лучшее" решение, но вы уже знаете это и просили что-то еще), заменив sys.excepthook, Что-то вроде

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

Если кто-то ищет быстрое минимальное решение,

import signal

# The code which crashes program on interruption

signal.signal(signal.SIGINT, call_this_function_if_interrupted)

# The code skipped if interrupted
Другие вопросы по тегам