Захват клавиатуры прерывается в Python без try-except

Есть ли способ в Python захватить событие KeyboardInterrupt, не помещая весь код внутри инструкции try - except? Я хочу чисто выйти без следа, если пользователь нажимает ctrl - c.

Ответ 1

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

import signal
import sys
import time

def signal_handler(signal, frame):
    print 'You pressed Ctrl+C!'
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print 'Press Ctrl+C'
while True:
    time.sleep(1)

Ответ 2

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

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

Ответ 3

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

>>> 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, сохраняя некоторое явное упоминание о том, что происходит.

Это также позволяет игнорировать прерывание только в некоторых частях вашего кода без необходимости устанавливать и reset снова обработчики сигналов каждый раз.

Ответ 4

Вы можете предотвратить печать трассировки стека для 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)

Ответ 5

Я знаю, что это старый вопрос, но я пришел сюда первым, а затем открыл модуль atexit. Я не знаю о его кросс-платформенной записи или полном списке предостережений, но пока это именно то, что я искал, пытаясь обработать post-KeyboardInterrupt cleanup в 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 ловит исключение, вызывает ваш обратный вызов (s), затем повторно вызывает это исключение, но вы можете сделать это по-другому.)

Подробнее см.: