2011-11-23 16 views
5

Il codice incollato sotto procede come segue:È un gestore di contesto adatto a questo lavoro?

  • crea un gancio importazione
  • crea un gestore di contesto che imposta la meta_path e pulisce in uscita.
  • dump tutte le importazioni effettuate da un programma passata in ingresso in imports.log

Ora mi chiedevo se si utilizza un gestore di contesto è una buona idea in questo caso, perché in realtà non ho lo standard Flusso try/finally, ma solo un set up e pulizia.

Un'altra cosa - con questa linea:

with CollectorContext(cl, sys.argv, 'imports.log') as cc: 

fa cc diventare None? Non dovrebbe essere un oggetto CollectorContext?

from __future__ import with_statement 
import os 
import sys 

class CollectImports(object): 
    """ 
    Import hook, adds each import request to the loaded set and dumps 
    them to file 
    """ 

    def __init__(self): 
     self.loaded = set() 

    def __str__(self): 
     return str(self.loaded) 

    def dump_to_file(self, fname): 
     """Dump the loaded set to file 
     """ 
     dumped_str = '\n'.join(x for x in self.loaded) 
     open(fname, 'w').write(dumped_str) 

    def find_module(self, module_name, package=None): 
     self.loaded.add(module_name) 


class CollectorContext(object): 
    """Sets the meta_path hook with the passed import hook when 
    entering and clean up when exiting 
    """ 

    def __init__(self, collector, argv, output_file): 
     self.collector = collector 
     self.argv = argv 
     self.output_file = output_file 

    def __enter__(self): 
     self.argv = self.argv[1:] 
     sys.meta_path.append(self.collector) 

    def __exit__(self, type, value, traceback): 
     # TODO: should assert that the variables are None, otherwise 
     # we are quitting with some exceptions 
     self.collector.dump_to_file(self.output_file) 
     sys.meta_path.remove(self.collector) 


def main_context(): 
    cl = CollectImports() 

    with CollectorContext(cl, sys.argv, 'imports.log') as cc: 
     progname = sys.argv[0] 
     code = compile(open(progname).read(), progname, 'exec') 
     exec(code) 


if __name__ == '__main__': 
    sys.argv = sys.argv[1:] 
    main_context() 
+0

Che cosa si intende per "non lo faccio hai il solito tentativo/finalmente flusso "? E cosa intendi con "cc diventa None"? –

+3

@DavidHeffernan Il motivo originale per i gestori di contesto era il codice refactoring che esegue 'try: setup_resource(); use_resource(); infine: cleanup_resource() '- Suppongo che sia ciò che significa" il solito flusso " – millimoose

+1

@Inerdial Forse, ma se è così, sembra che il codice precedente senza try/finally fosse sbagliato e avrebbe dovuto provare/finalmente. –

risposta

1

Grazie a tutti ora funziona senza problemi, in realtà ho voluto con a restituire qualcosa perché volevo incapsulare il "run "all'interno del gestore di contesto, quindi ottengo qualcosa come di seguito.

Inoltre, ora devo conservare il vecchio sys.argv e ripristinarlo in uscita, probabilmente non fondamentale ma ancora una bella cosa da fare credo ..

class CollectorContext(object): 
    """Sets the meta_path hook with the passed import hook when 
    entering and clean up when exiting 
    """ 

    def __init__(self, collector, argv, output_file): 
     self.collector = collector 
     self.old_argv = argv[:] 
     self.output_file = output_file 
     self.progname = self.old_argv[1] 

    def __enter__(self): 
     sys.argv = self.old_argv[1:] 
     sys.meta_path.append(self.collector) 
     return self 

    def __exit__(self, type, value, traceback): 
     # TODO: should assert that the variables are None, otherwise 
     # we are quitting with some exceptions 
     self.collector.dump_to_file(self.output_file) 
     sys.meta_path.remove(self.collector) 
     sys.argv = self.old_argv[:] 

    def run(self): 
     code = compile(open(self.progname).read(), self.progname, 'exec') 
     exec(code) 


def main_context(): 
    cl = CollectImports() 

    with CollectorContext(cl, sys.argv, 'imports.log') as cc: 
     cc.run() 
3

Penso che questo concetto sia ok. Inoltre, non vedo alcun motivo per non dover fare i clean-up in una clausola finally:, quindi il gestore di contesto si adatta perfettamente.

Il tuo cc è None, perché hai detto di essere così.

Se non si desidera che, cambiare il vostro __enter__ method to return something else:

Il valore restituito da questo metodo è legato alla identificatore nella clausola as di with dichiarazioni che utilizzano questo gestore di contesto.

def __enter__(self): 
    self.argv = self.argv[1:] 
    sys.meta_path.append(self.collector) 
    return self 
    # or 
    return self.collector 
    # or 
    return "I don't know what to return here" 

e poi

with CollectorContext(cl, sys.argv, 'imports.log') as cc: 
    print cc, repr(cc) # there you see what happens. 
    progname = sys.argv[0] 
    code = compile(open(progname).read(), progname, 'exec') 
    exec(code) 
2

Se si vuole sempre la pulizia a verificarsi, è necessario utilizzare un gestore di contesto. Non sono sicuro di dove si utilizza try..finally se si implementa il gestore di contesto utilizzando i metodi speciali di basso livello. Se si utilizza il decoratore @contextmanager, il gestore del contesto viene codificato in un modo "naturale", quindi è dove si utilizza try..finally anziché ottenere l'eccezione come parametro.

Inoltre, cc sarà il valore restituito da __enter__(). Nel tuo caso, None. Il modo in cui comprendo la progettazione del gestore di contesto è che il valore restituito è il "contesto". Ciò che fa il gestore di contesto è configurare e ripulire i contesti in cui accade qualcos'altro. Per esempio. una connessione al database creerà le transazioni e le operazioni del database avvengono nell'ambito di tali transazioni.

Detto questo, quanto sopra è proprio lì per offrire la massima flessibilità. Non c'è niente di sbagliato semplicemente creando un contesto (che gestisce se stesso) direttamente e restituendo self, o anche non restituendo nulla se non è necessario utilizzare il valore di contesto all'interno dello with.Dal momento che non si utilizza cc da nessuna parte, si può solo fare e non preoccuparti per il valore di ritorno:

with CollectorContext(cl, sys.argv, 'imports.log'): 
     progname = sys.argv[0] 
     code = compile(open(progname).read(), progname, 'exec') 
     exec(code) 
Problemi correlati