Czy w Pythonie jest sposób na przechwycenie KeyboardInterrupt
zdarzenia bez umieszczania całego kodu w instrukcji try
- except
?
Chcę czysto wyjść bez śladu, jeśli użytkownik naciśnie Ctrl+ C.
Odpowiedzi:
Tak, możesz zainstalować program obsługi przerwań za pomocą sygnału modułu i czekać wiecznie używając wątków .
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
. (W tym stylu i tak while True: pass
byłoby schludniej.) Byłoby to bardzo marnotrawne; spróbuj czegoś w rodzaju while True: time.sleep(60 * 60 * 24)
(spanie przez jeden dzień jest całkowicie arbitralną postacią).
time
(tak, jak powinieneś), nie zapomnij o import time
:)
Jeśli chcesz tylko nie pokazywać śledzenia, zrób swój kod w ten sposób:
## all your app logic here
def main():
## whatever your app does.
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
# do nothing here
pass
(Tak, wiem, że to nie jest bezpośrednią odpowiedzią na pytanie, ale nie jest do końca jasne, dlaczego potrzeba blokowania prób / wyjątków jest niepożądana - może to sprawia, że jest mniej irytująca dla OP)
signal.signal( signal.SIGINT, lambda s, f : sys.exit(0))
zawsze to robi.
Alternatywą dla ustawienia własnego modułu obsługi sygnału jest użycie menedżera kontekstu do przechwycenia wyjątku i zignorowania go:
>>> 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
...
>>>
To usuwa blok try
- except
, zachowując jednocześnie wyraźną wzmiankę o tym, co się dzieje.
Pozwala to również zignorować przerwanie tylko w niektórych częściach kodu bez konieczności ustawiania i resetowania za każdym razem programów obsługi sygnału.
Wiem, że to stare pytanie, ale najpierw przyszedłem tutaj, a potem odkryłem atexit
moduł. Nie wiem jeszcze o jego osiągnięciach na wielu platformach ani o pełnej liście zastrzeżeń, ale jak dotąd jest to dokładnie to, czego szukałem, próbując obsłużyć po KeyboardInterrupt
czyszczeniu w systemie Linux. Chciałem tylko rzucić okiem na inny sposób podejścia do problemu.
Chcę przeprowadzić porządki po wyjściu z programu w kontekście operacji Fabric, więc zawijanie wszystkiego w try
/ except
nie było dla mnie opcją. Wydaje mi się, że atexit
może być dobrym rozwiązaniem w takiej sytuacji, w której Twój kod nie znajduje się na najwyższym poziomie przepływu kontroli.
atexit
jest bardzo wydajny i czytelny po wyjęciu z pudełka, na przykład:
import atexit
def goodbye():
print "You are now leaving the Python sector."
atexit.register(goodbye)
Możesz również użyć go jako dekoratora (od 2.6; ten przykład pochodzi z dokumentacji):
import atexit
@atexit.register
def goodbye():
print "You are now leaving the Python sector."
Jeśli chcesz, aby było to specyficzne KeyboardInterrupt
tylko dla, odpowiedź innej osoby na to pytanie jest prawdopodobnie lepsza.
Należy jednak pamiętać, że atexit
moduł zawiera tylko ~ 70 linii kodu i nie byłoby trudno stworzyć podobną wersję, która traktuje wyjątki inaczej, na przykład przekazując wyjątki jako argumenty do funkcji wywołania zwrotnego. (Ograniczenie atexit
to gwarantowałoby zmodyfikowaną wersję: obecnie nie mogę sobie wyobrazić sposobu, w jaki funkcje wywołania zwrotnego wyjścia miałyby wiedzieć o wyjątkach; program atexit
obsługi przechwytuje wyjątek, wywołuje wywołanie zwrotne, a następnie ponownie wywołuje ten wyjątek. Ale możesz zrobić to inaczej).
Więcej informacji:
atexit
działa zarówno dla MacOS, jak i Ubuntu 18.04 dla Pythona 3.7 i 3.8
Możesz zapobiec drukowaniu śladu stosu KeyboardInterrupt
bez try: ... except KeyboardInterrupt: pass
(najbardziej oczywistego i prawdopodobnie „najlepszego” rozwiązania, ale już go znasz i prosiłeś o coś innego), zastępując sys.excepthook
. Coś jak
def custom_excepthook(type, value, traceback):
if type is KeyboardInterrupt:
return # do nothing
else:
sys.__excepthook__(type, value, traceback)
Wypróbowałem sugerowane rozwiązania przez wszystkich, ale musiałem sam improwizować kod, aby faktycznie zadziałał. Oto mój improwizowany kod:
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