2009-07-10 2 views
393

Sto lavorando a uno script python che avvia diversi processi e connessioni al database. Ogni tanto mi vogliono uccidere lo script con un + C segnale Ctrl, e mi piacerebbe fare un po 'di pulizia.Come posso acquisire SIGINT in Python?

In Perl farei questo:

$SIG{'INT'} = 'exit_gracefully'; 

sub exit_gracefully { 
    print "Caught ^C \n"; 
    exit (0); 
} 

Come posso fare l'analogo di questo in Python?

+3

Assicurati di vedere: http://blip.tv/file/2232410 (Mindblowing Python GIL) prima di fili e segnali di miscelazione (se si ha non già, ovviamente) . Le diapositive sono qui: http://www.dabeaz.com/python/GIL.pdf. –

risposta

561

Registra l'handler con signal.signal come questo:

#!/usr/bin/env python 
import signal 
import sys 
def signal_handler(signal, frame): 
     print('You pressed Ctrl+C!') 
     sys.exit(0) 
signal.signal(signal.SIGINT, signal_handler) 
print('Press Ctrl+C') 
signal.pause() 

Codice adattato da here.

È possibile trovare ulteriore documentazione su signalhere.

+0

-1 per ciclo infinito. Vedere la risposta di Chad sotto a signal.pause() e si prega di aggiornare. – Shabbyrobe

+60

Non chiaro perché non va bene il ciclo infinito nel codice demo, ma comunque aggiornato. –

+8

Potresti dirmi perché utilizzare questo al posto di un'eccezione KeyboardInterrupt? Non è più intuitivo da usare? – noio

21

è possibile gestire CTRL +C catturando l'eccezione KeyboardInterrupt. È possibile implementare qualsiasi codice di pulizia nel gestore di eccezioni.

136

È possibile trattarlo come un'eccezione (KeyboardInterrupt), come tutti gli altri. Fare un nuovo file ed eseguirlo dalla shell con il seguente contenuto per capire cosa intendo:

import time, sys 

x = 1 
while True: 
    try: 
     print x 
     time.sleep(.3) 
     x += 1 
    except KeyboardInterrupt: 
     print "Bye" 
     sys.exit() 
+12

Attenzione quando si usa questa soluzione. Dovresti usare questo codice anche prima del blocco catch di KeyboardInterrupt: 'signal.signal (signal.SIGINT, signal.default_int_handler)', altrimenti fallirai, perché KeyboardInterrupt non sparerà in ogni situazione in cui dovrebbe sparare! I dettagli sono [qui] (http://stackoverflow.com/a/40785230/1908192). – Velda

4

È possibile utilizzare le funzioni di Python incorporata signal module di istituire gestori di segnali in python. In particolare, la funzione signal.signal(signalnum, handler) viene utilizzata per registrare la funzione handler per il segnale signalnum.

17

Da Python 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 
49

E come contesto allenatore:

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 

Per utilizzare:

with GracefulInterruptHandler() as h: 
    for i in xrange(1000): 
     print "..." 
     time.sleep(1) 
     if h.interrupted: 
      print "interrupted!" 
      time.sleep(2) 
      break 

gestori nidificate:

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

+1

Ho aggiunto il mio stesso sapore https://gist.github.com/camilin87/8571791 –

+0

Potrebbe anche lanciare un 'StopIteration' per interrompere il ciclo più interno quando viene premuto un Ctrl-C, giusto? –

+0

@TheoBelaire Invece di lanciare una StopIteration, creerei un generatore che accetta un iterabile come parametro e registra/rilascia il gestore di segnale. – Udi

9

Ancora un altro frammento

Denominato main come la funzione principale e exit_gracefully come CTRL +c gestore

if __name__ == '__main__': 
    try: 
     main() 
    except KeyboardInterrupt: 
     pass 
    finally: 
     exit_gracefully() 
+3

Si dovrebbe usare solo eccetto per cose che non dovrebbero accadere. In questo caso, si suppone che KeyboardInterrupt si verifichi. Quindi questa non è una buona costruzione. –

+4

@TristanT In qualsiasi altra lingua si, ma in Python le eccezioni non sono solo per cose che non dovrebbero accadere. In realtà è considerato un buon stile in Python per usare le eccezioni per il controllo del flusso (se appropriato). –

5

ho adattato il codice @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 tastiera interrupt (SIGINT) e il SIGTERM (kill <process>)

2

In contrasto Matt J sua risposta, uso un semplice oggetto. 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