2013-08-23 27 views
5

Dal datamodel docs sul contesto manager:ri-sollevare un'eccezione in un gestore contesto

noti che __exit__() metodi non dovrebbero rilanciare l'eccezione passata-in; questa è la responsabilità del chiamante.


Ho un file temporaneo, il cui descrittore di file vorrei libero con close ma senza scrivere nulla sul disco. La mia soluzione intuitiva era di passare l'eccezione, ma questo è discouraged in the docs - e sicuramente per buoni motivi.

class Processor(object): 
    ... 
    def write(self, *args, **kwargs): 
     if something_bad_happens: 
      raise RuntimeError('This format expects %s columns: %s, got %s.' % (
           (len(self.cols), self.cols, len(args)))) 
     self.writer.writerow(args) 

    def __enter__(self): 
     return self 

    def __exit__(self, type, value, traceback): 
     # the RuntimeError from write will be set as type, value and so on .. 
     # I'd like to close the stream here (release the file descriptor), 
     # but I do not leave a trace of the associated file - 
     # (one can always 'manually' delete with `os.remove` but maybe there's a 
     # better way ..?) 
     self.output_pipe.close() 

Anche io non voglio in questo particolare la gestione degli errori caso il chiamante per due motivi:

  • per mantenere il codice del chiamante minima (vedi sotto)
  • il chiamante è felice con eccezioni (fail veloce è quello che vogliamo qui)

Il manager contesto viene utilizzato o meno così:

class Worker(object): 
    ... 
    def run(self): 
     # output setup so it will emit a three column CSV 
     with self.output().open('w') as output: 
      output.write('John', 'CA', 92101) 
      output.write('Jane', 'NY', 10304) 
      # should yield an error, since only three 'columns' are allowed 
      output.write('Hello', 'world') 

aggiornamento: Il mio quesion era un po 'mal formulato come il mio problema in realtà si riduceva a questo: In contesto nidificato manager, come posso passare un'eccezione al CM più esterno?

+1

Intendevi davvero chiudere l'output due volte in '__exit__'? – user2357112

+0

@ user2357112, c'è altro codice che non ho incluso qui - quindi il codice potrebbe apparire terso e forse difficile da contestualizzare, mi dispiace per quello. Aggiornata la mia domanda, non chiudo 'output_pipe' due volte. – miku

risposta

6
  • Quando __exit__ restituisce True, qualsiasi eccezione passata viene ingerita.
  • Quando __exit__ restituisce False, l'eccezione viene controrilanciata.

def __exit__(self, type, value, traceback): 
    self.output_pipe.close() # always close the file 
    if type is not None: # an exception has occurred 
     os.unlink(...) # remove the file 
     return False  # reraise the exception 

Si potrebbe naturalmente omettere il return False dal Python tornerà None (che è Falsish) per impostazione predefinita.


Tra l'altro, è self.output() un'istanza di Processor? Se è così,

with self.output().open('w') as output: 

dovrebbe essere

with self.output() as output: 

In ogni caso, sarebbe bello se si potesse organizzare per quest'ultimo di essere la sintassi corretta. Potrebbe essere necessario cambiare __enter__ a:

def __enter__(self): 
    return self.output_pipe.open('w') 
+0

Grazie, credo che il 'return False' è solo quello che ho cercato. – miku

+1

@miku: * Qualsiasi * valore falso farà; 'Nessuno' incluso; restituire esplicitamente significa che viene restituito "Nessuno" e l'eccezione rimane sollevata. –

+1

@MartijnPieters, vedo. Trovo che l'esplicito 'False' sia un po 'più credibile al momento. Devo controllare la libreria standard per le convenzioni su questo ... – miku

2

C'è no necessario raise l'eccezione; l'eccezione è già stata sollevata e il gestore di contesto viene semplicemente informato di ciò.

di prova se non ci fosse alcun un'eccezione invece:

if type is None: 
    # if no exception is raised, proceed as usual: 
    self.output_pipe.close() 

Se il tuo manager contesto tornasse True in quel momento si sarebbe sopprimere l'eccezione; l'uscita dalla funzione restituisce invece None e l'eccezione rimane 'sollevata'.

Si noti che il tempfile module include due tipi di oggetti file temporanei che fungono da gestori di contesto, che auto-eliminano già, indipendente dalla piattaforma. Sui sistemi POSIX è possibile scollegare il file subito dopo la creazione; il descrittore di file rimane attivo fino alla chiusura del file. Windows offre anche un'opzione "elimina alla chiusura". La classe tempfile.NamedTemporaryFile() utilizza l'opzione giusta per la tua piattaforma.

+0

Sembra buono e farebbe quello che voglio, ma libera la risorsa (descrittore di file) di "output_pipe" che in quel momento è un file aperto in modalità 'w'? – miku

+0

Perché testare? – user2357112

+0

@miku: Sembra che il tuo codice chiama 'self.output_pipe.close()' ** due volte **; la seconda volta solo quando non hai riscontrato un'eccezione. Ho semplicemente mostrato come gestire le eccezioni. –

Problemi correlati