Есть ли способ в Python захватить KeyboardInterrupt
событие, не помещая весь код в оператор try
- except
?
Я хочу выйти без следа, если пользователь нажмет Ctrl+ C.
Ответы:
Да, вы можете установить обработчик прерывания, используя сигнал модуля , и ждать вечно, используя потоки .
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()
while True: continue
. (В while True: pass
любом случае, в таком стиле было бы аккуратнее.) Это было бы очень расточительно; попробуйте что-нибудь вроде while True: time.sleep(60 * 60 * 24)
(спать по день - это совершенно произвольная цифра).
time
(как следует), не забудьте import time
:)
Если все, что вы хотите, - это не отображать трассировку, сделайте свой код следующим образом:
## 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)
signal.signal( signal.SIGINT, lambda s, f : sys.exit(0))
всегда делает.
Альтернативой настройке собственного обработчика сигналов является использование диспетчера контекста для перехвата исключения и его игнорирования:
>>> 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
обработчик улавливает исключение, вызывает ваши обратные вызовы, а затем повторно вызывает это исключение. Но вы могли бы сделать это по-другому.)
Для получения дополнительной информации см .:
atexit
atexit
работу как для MacOS, так и для Ubuntu 18.04 для python 3.7 и 3.8
Вы можете предотвратить печать трассировки стека 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
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