C'è qualche modo in Python per catturare l'evento KeyboardInterrupt
senza inserire tutto il codice all'interno di una dichiarazione try
- except
? Voglio uscire senza lasciare traccia se l'utente preme ctrl - c.Capture keyboardinterrupt in Python senza try-except
risposta
Sì, è possibile installare un gestore di interrupt utilizzando il signal module.
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)
È possibile evitare che la stampa di una traccia dello stack per KeyboardInterrupt
senza try: ... except KeyboardInterrupt: pass
(il più evidente e stata riordinata la soluzione "migliore", ma si conosce già e chiesto qualcos'altro) sostituendo sys.excepthook
. Qualcosa di simile
def custom_excepthook(type, value, traceback):
if type is KeyboardInterrupt:
return # do nothing
else:
sys.__excepthook__(type, value, traceback)
voglio uscire pulito senza lasciare traccia se l'utente preme ctrl-c – Alex
rilevato ==> rilevato –
Questo non è affatto vero. L'eccezione KeyboardInterrupt viene creata durante un gestore di interrupt. Il gestore predefinito per SIGINT solleva il KeyboardInterrupt, quindi se non si desidera quel comportamento tutto ciò che si dovrebbe fare è fornire un gestore di segnale diverso per SIGINT. I tuoi sono corretti in quanto le eccezioni possono essere gestite solo in una prova/eccetto tuttavia in questo caso puoi impedire che l'eccezione venga mai sollevata in primo luogo. – Matt
Se invece si è di non mostrare il traceback, rendere il codice in questo modo:
## all your app logic here
def main():
## whatever your app does.
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
# do nothing here
pass
(Sì, lo so che questo non risponde direttamente alla domanda, ma non è davvero chiaro perché provare/escludere il blocco è discutibile - forse questo lo rende meno fastidioso per l'OP)
Per qualche motivo, questo non sempre funziona per me. 'signal.signal (signal.SIGINT, lambda s, f: sys.exit (0))' sempre. –
Questo non funziona sempre con cose come pygtk che usano i thread. A volte^C uccide solo il thread corrente invece dell'intero processo, quindi l'eccezione si propagherà solo attraverso quel thread. –
C'è un'altra domanda SO specifica su Ctrl + C con pygtk: http://stackoverflow.com/questions/16410852/keyboard-interrupt-with-with-python-gtk – bgporter
Un'alternativa all'impostazione del proprio gestore di segnale consiste nell'utilizzare un gestore di contesto per rilevare l'eccezione e ignorarla:
>>> 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
...
>>>
Questo rimuove il blocco try
- except
pur mantenendo una menzione esplicita di ciò che sta accadendo.
Ciò consente inoltre di ignorare l'interrupt solo in alcune parti del codice senza dover impostare e reimpostare nuovamente i gestori di segnale ogni volta.
nice, questa soluzione sembra un po 'più diretta nell'esprimere il scopo piuttosto che affrontare i segnali. – Seaux
So che questa è una vecchia domanda ma sono venuto qui prima e ho scoperto il modulo atexit
. Non so ancora quale sia il suo track record cross-platform o un elenco completo di avvertimenti, ma finora è esattamente quello che stavo cercando nel tentativo di gestire post-KeyboardInterrupt
cleanup su Linux. Volevo solo introdurre un altro modo per affrontare il problema.
Desidero eseguire la pulizia post-uscita nel contesto delle operazioni Fabric, quindi avvolgere tutto in try
/except
non era un'opzione neanche per me. Mi sento come atexit
potrebbe essere una buona misura in una situazione del genere, in cui il codice non è al livello più alto del flusso di controllo.
atexit
è molto capace e leggibile fuori dalla scatola, per esempio:
import atexit
def goodbye():
print "You are now leaving the Python sector."
atexit.register(goodbye)
È possibile anche usarlo come un decoratore (a partire da 2.6, questo esempio è dalla documentazione):
import atexit
@atexit.register
def goodbye():
print "You are now leaving the Python sector."
Se si desidera renderlo specifico solo per KeyboardInterrupt
, la risposta di un'altra persona a questa domanda è probabilmente migliore.
Ma si noti che il modulo atexit
è solo ~ 70 righe di codice e non sarebbe difficile creare una versione simile che tratta le eccezioni in modo diverso, ad esempio passando le eccezioni come argomenti alle funzioni di callback.(La limitazione di atexit
che giustificherebbe una versione modificata: al momento non riesco a immaginare un modo per le funzioni di callback di uscita per conoscere le eccezioni: il gestore atexit
rileva l'eccezione, chiama il tuo callback (s), quindi .. -raises che un'eccezione ma si poteva fare in modo diverso)
Per maggiori informazioni vedi:
- Official documentation on
atexit
- Il Python Module of the Week post, una buona introduzione
Si noti che ci sono alcuni problemi specifici della piattaforma con il modulo del segnale - non dovrebbe influenzare questo poster, ma "Su Windows, signal() può essere chiamato solo con SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV o SIGTERM. In caso contrario verrà generato un errore ValueError. " – bgporter
Funziona bene anche con i fili. Spero che tu non faccia mai 'while True: continue', però. (In quello stile, 'while True: pass' sarebbe più ordinato, comunque.) Sarebbe molto dispendioso; prova qualcosa come 'while True: time.sleep (60 * 60 * 24)' (dormire per un giorno alla volta è una cifra completamente arbitraria). –
Se si sta utilizzando il suggerimento di Chris Morgan di usare 'time' (come si dovrebbe), non dimenticare di importare il tempo :) :) – Seaux