2016-01-22 16 views
15

Scrivo un server che gestisce gli eventi e le eccezioni non rilevate durante la gestione dell'evento non deve terminare il server.Robusto loop infinito per server scritto in Python

Il server è un singolo processo Python senza thread.

voglio terminare su questi tipi di errori:

  • KeyboardInterrupt
  • MemoryError
  • ...

La lista di costruito in eccezioni è lungo: https://docs.python.org/2/library/exceptions.html

Non voglio re-inventare questa gestione delle eccezioni, dal momento che immagino sia stata eseguita sever altre volte prima.

Come procedere?

  1. Avere una white-list: un elenco di eccezioni, che sono ok e l'elaborazione dell'evento successivo è la scelta giusta
  2. Avere una lista nera: un elenco di eccezioni che indicano che chiude il server è il diritto scelta.

Suggerimento: questa domanda non riguarda l'esecuzione di un daemon unix in background. Non si tratta di doppia forcella e non si tratta di standard input riorientare/stdout :-)

risposta

2

il più in alto eccezione è BaseException. Ci sono due gruppi sotto che:

  • Exception derivato
  • tutto il resto

cose come Stopiteration, ValueError, TypeError, ecc, sono tutti esempi di Exception.

Cose come GeneratorExit, SystemExit e KeyboardInterrupt non discendono da Execption.

Quindi il primo passo è catturare Exception e non BaseException che consentirà di terminare facilmente il programma. Raccomando di prendere anche GeneratorExit come 1) non dovrebbe mai essere visto a meno che non venga sollevato manualmente; 2) puoi loggarlo e riavviare il ciclo; e 3) è inteso per segnalare che un generatore è uscito e può essere ripulito, non che il programma dovrebbe uscire.

Il passaggio successivo consiste nel registrare ogni eccezione con un numero di dettagli sufficiente a consentire di capire cosa è andato storto (quando in seguito si passa al debug).

Infine, si deve decidere per te stesso che, se del caso, delle Exception eccezioni derivati ​​si desidera terminare il: vorrei suggerire RuntimeError e MemoryError, anche se si può essere in grado di ottenere intorno a quelli semplicemente l'arresto e il riavvio loop del server.

Quindi, davvero, dipende da voi.

Se c'è qualche altro errore (ad esempio IOError quando si cerca di caricare un file di configurazione) che è abbastanza seria per uscire, poi il codice responsabile del caricamento del file di configurazione dovrebbe essere abbastanza intelligente per catturare quel IOError ed aumentare SystemExit anziché.

Per quanto riguarda la lista bianca/lista nera - utilizzare una lista nera, in quanto dovrebbero esserci solo una manciata, se esistente, di eccezioni basate su Exception che è necessario in realtà terminare il server.

+1

Questo potrebbe anche essere utile per decidere quale delle 'Exception's da gestire: gerarchia delle eccezioni incorporate: https://docs.python.org/3.5/library/exceptions.html#exception-hierarchy – Timur

4

vorrei farlo in un modo simile stai pensando di, utilizzando il 'tu non passerai' Gandalf gestore di eccezioni except Exception to catch all non-system-exiting exceptions durante la creazione di una lista nera set di eccezioni che dovrebbero passare e terminare nuovamente.

Uso della Gandalf gestorefarà in modo GeneratorExit, SystemExit e KeyboardInterrupt (tutte le eccezioni di sistema uscendo) passare e terminare il programma se nessun altro gestori sono presenti più alto nello stack di chiamate. Qui è dove è possibile verificare con type(e) che un'eccezione rilevata e appartiene effettivamente al set di eccezioni elencate in nero e ri-raise.

Come una piccola dimostrazione:

import exceptions # Py2.x only 

# dictionary holding {exception_name: exception_class} 
excptDict = vars(exceptions) 

exceptionNames = ['MemoryError', 'OSError', 'SystemError'] # and others 

# set containing black-listed exceptions 
blackSet = {excptDict[exception] for exception in exceptionNames} 

Ora blackSet = {OSError, SystemError, MemoryError} tenendo le classi del non-sistema-uscendo eccezioni che vogliamo non maniglia.

Un try-except blocco può apparire così:

try: 
    # calls that raise exceptions: 
except Exception as e: 
    if type(e) in blackSet: raise e # re-raise 
    # else just handle it 

Un esempio che cattura tutte le eccezioni che utilizzano BaseException può aiutare a illustrare quello che voglio dire. (Questo è fatto a scopo dimostrativo solo, al fine di vedere come questa raccolta finirà per terminare il programma).Nota: Sono non suggerendo di utilizzare BaseException; Lo sto usando al fine di dimostrare la quello eccezione sarà effettivamente 'passare attraverso' e causare la terminazione (vale a dire tutto ciò che BaseException catture):

for i, j in excptDict.iteritems(): 
    if i.startswith('__'): continue # __doc__ and other dunders 
    try: 
     try: 
      raise j 
     except Exception as ex: 
      # print "Handler 'Exception' caught " + str(i) 
      if type(ex) in blackSet: 
       raise ex   
    except BaseException: 
     print "Handler 'BaseException' caught " + str(i) 

# prints exceptions that would cause the system to exit  
Handler 'BaseException' caught GeneratorExit 
Handler 'BaseException' caught OSError 
Handler 'BaseException' caught SystemExit 
Handler 'BaseException' caught SystemError 
Handler 'BaseException' caught KeyboardInterrupt 
Handler 'BaseException' caught MemoryError 
Handler 'BaseException' caught BaseException 

Infine, al fine di rendere questo Python 2/3 agnostici , è possibile try e import exceptions e se ciò non riesce (cosa che fa in Python 3), fall-back per importare builtins che contiene tutto Exceptions; si cerca il dizionario dal nome in modo non fa alcuna differenza:

try: 
    import exceptions 
    excDict = vars(exceptions) 
except ImportError: 
    import builtins 
    excDict = vars(builtins) 

Non so se c'è un modo più intelligente di fare effettivamente questa, un'altra soluzione potrebbe essere, invece di avere un try-except con un signle except, avendo 2 gestori, uno per le eccezioni black list e l'altro per il caso generale:

try: 
    # calls that raise exceptions: 
except tuple(blackSet) as be: # Must go first, of course. 
    raise be 
except Exception as e: 
    # handle the rest