Sto lavorando su uno script python che avvia diversi processi e connessioni al database. Ogni tanto voglio uccidere la sceneggiatura con a Ctrl+C segnale, e mi piacerebbe fare un po 'di pulizia.
In Perl farei questo:
$SIG{'INT'} = 'exit_gracefully';
sub exit_gracefully {
print "Caught ^C \n";
exit (0);
}
Come faccio a fare l'analogo di questo in Python?
Registra il tuo gestore con signal.signal
in questo modo:
#!/usr/bin/env python
import signal
import sys
def signal_handler(sig, frame):
print('You pressed Ctrl+C!')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
signal.pause()
Codice adattato da qui .
Più documentazione su signal
può essere trovata qui .
Puoi trattarlo come un'eccezione (KeyboardInterrupt), come qualsiasi altra. Crea un nuovo file ed eseguilo dalla tua shell con i seguenti contenuti per vedere cosa intendo:
import time, sys
x = 1
while True:
try:
print x
time.sleep(.3)
x += 1
except KeyboardInterrupt:
print "Bye"
sys.exit()
E come gestore del contesto:
import signal
class GracefulInterruptHandler(object):
def __init__(self, sig=signal.SIGINT):
self.sig = sig
def __enter__(self):
self.interrupted = False
self.released = False
self.original_handler = signal.getsignal(self.sig)
def handler(signum, frame):
self.release()
self.interrupted = True
signal.signal(self.sig, handler)
return self
def __exit__(self, type, value, tb):
self.release()
def release(self):
if self.released:
return False
signal.signal(self.sig, self.original_handler)
self.released = True
return True
Usare:
with GracefulInterruptHandler() as h:
for i in xrange(1000):
print "..."
time.sleep(1)
if h.interrupted:
print "interrupted!"
time.sleep(2)
break
Gestori annidati:
with GracefulInterruptHandler() as h1:
while True:
print "(1)..."
time.sleep(1)
with GracefulInterruptHandler() as h2:
while True:
print "\t(2)..."
time.sleep(1)
if h2.interrupted:
print "\t(2) interrupted!"
time.sleep(2)
break
if h1.interrupted:
print "(1) interrupted!"
time.sleep(2)
break
Da qui: https://Gist.github.com/2907502
Puoi gestire CTRL+C catturando l'eccezione KeyboardInterrupt
. È possibile implementare qualsiasi codice di pulizia nel gestore di eccezioni.
Da Python's documentation :
import signal
import time
def handler(signum, frame):
print 'Here you go'
signal.signal(signal.SIGINT, handler)
time.sleep(10) # Press Ctrl+c here
Riferito main
come funzione principale e exit_gracefully
come CTRL + c gestore
if __== '__main__':
try:
main()
except KeyboardInterrupt:
pass
finally:
exit_gracefully()
Ho adattato il codice da @udi per supportare più segnali (niente di speciale):
class GracefulInterruptHandler(object):
def __init__(self, signals=(signal.SIGINT, signal.SIGTERM)):
self.signals = signals
self.original_handlers = {}
def __enter__(self):
self.interrupted = False
self.released = False
for sig in self.signals:
self.original_handlers[sig] = signal.getsignal(sig)
signal.signal(sig, self.handler)
return self
def handler(self, signum, frame):
self.release()
self.interrupted = True
def __exit__(self, type, value, tb):
self.release()
def release(self):
if self.released:
return False
for sig in self.signals:
signal.signal(sig, self.original_handlers[sig])
self.released = True
return True
Questo codice supporta la chiamata interrupt di tastiera (SIGINT
) e SIGTERM
(kill <process>
)
Puoi usare le funzioni nel modulo di segnale integrato di Python per impostare i gestori di segnale in python. In particolare, la funzione signal.signal(signalnum, handler)
viene utilizzata per registrare la funzione handler
per il segnale signalnum
.
In contrasto con Matt J la sua risposta, io uso un oggetto semplice. Questo mi dà la possibilità di analizzare questo gestore per tutti i thread che devono essere fermati in sicurezza.
class SIGINT_handler():
def __init__(self):
self.SIGINT = False
def signal_handler(self, signal, frame):
print('You pressed Ctrl+C!')
self.SIGINT = True
handler = SIGINT_handler()
signal.signal(signal.SIGINT, handler.signal_handler)
Altrove
while True:
# task
if handler.SIGINT:
break
grazie per le risposte esistenti, ma aggiunto signal.getsignal()
import signal
# store default handler of signal.SIGINT
default_handler = signal.getsignal(signal.SIGINT)
catch_count = 0
def handler(signum, frame):
global default_handler, catch_count
catch_count += 1
print ('wait:', catch_count)
if catch_count > 3:
# recover handler for signal.SIGINT
signal.signal(signal.SIGINT, default_handler)
print('expecting KeyboardInterrupt')
signal.signal(signal.SIGINT, handler)
print('Press Ctrl+c here')
while True:
pass
Personalmente, non ho potuto usare try/except KeyboardInterrupt perché stavo usando la modalità socket standard (IPC) che sta bloccando. Quindi il SIGINT è stato cueued, ma è arrivato solo dopo aver ricevuto i dati sul socket.
L'impostazione di un gestore di segnale si comporta allo stesso modo.
D'altra parte, questo funziona solo per un vero terminale. Altri ambienti di partenza potrebbero non accettare Ctrl + C o pre-gestire il segnale.
Inoltre, in Python esistono "Eccezioni" e "BaseExceptions", che differiscono nel senso che l'interprete deve uscire in modo pulito, quindi alcune eccezioni hanno una priorità più alta di altre (Eccezioni derivate da BaseException)