Capture keyboardinterrupt в Python без try-except


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

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

5 92

5 ответов:

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

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

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

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

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