2010-11-17 11 views

risposta

122

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) 
+8

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

+6

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). –

+1

Se si sta utilizzando il suggerimento di Chris Morgan di usare 'time' (come si dovrebbe), non dimenticare di importare il tempo :) :) – Seaux

4

È 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) 
+0

voglio uscire pulito senza lasciare traccia se l'utente preme ctrl-c – Alex

+0

rilevato ==> rilevato –

+7

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

25

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)

+2

Per qualche motivo, questo non sempre funziona per me. 'signal.signal (signal.SIGINT, lambda s, f: sys.exit (0))' sempre. –

+0

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. –

+0

C'è un'altra domanda SO specifica su Ctrl + C con pygtk: http://stackoverflow.com/questions/16410852/keyboard-interrupt-with-with-python-gtk – bgporter

23

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.

+1

nice, questa soluzione sembra un po 'più diretta nell'esprimere il scopo piuttosto che affrontare i segnali. – Seaux

4

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: