2011-11-23 7 views
5

El código pegado a continuación hace lo siguiente:¿Es un administrador de contexto adecuado para este trabajo?

  • crea un gancho de importación
  • crea un gestor de contexto que establece el meta_path y limpia a la salida.
  • vertederos de todas las importaciones realizadas por un programa aprobado en la entrada en imports.log

Ahora me preguntaba si se utiliza un gestor de contexto es una buena idea en este caso, porque en realidad no tengo la norma try/finally flujo, pero solo una configuración y limpieza.

Otra cosa - con esta línea:

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

qué cc convierten None? ¿No debería ser un objeto 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

¿Qué quiere decir con "no lo hago tener el try/finally flow habitual "? ¿Y a qué te refieres con "does cc become None"? –

+3

@DavidHeffernan La razón original para los administradores de contexto era refactorizar el código que '' try: setup_resource(); use_resource(); Finalmente: cleanup_resource() '- Supongo que eso es lo que significa" flujo habitual " – millimoose

+1

@Inerdial Tal vez, pero si es así, parece que el código anterior sin try/finally era incorrecto y debería haber sido try/finally. –

Respuesta

1

Gracias a todos ahora funciona sin problemas, yo en realidad quería volver con algo porque quería encapsular el "run "dentro del administrador de contexto, entonces obtengo algo como a continuación".

otra parte, ahora almaceno el viejo sys.argv y restaurarlo a la salida, probablemente no fundamental, pero sigue siendo una buena cosa que hacer creo ..

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

creo que este concepto está bien. Además, no veo ninguna razón para no tener las cosas de limpieza en una cláusula finally:, por lo que el administrador de contexto se adapta perfectamente.

Su cc es None, porque usted le dijo que así fuera.

Si no quieres eso, cambiar su __enter__ method to return something else:

El valor devuelto por este método está ligada al identificador en el as cláusula de with declaraciones usar este gestor de contexto.

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" 

y luego

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

Si siempre desea la limpieza que ocurra, se debe utilizar un gestor de contexto. No estoy seguro de dónde usa try..finally si implementa el administrador de contexto utilizando los métodos especiales de bajo nivel. Si usa el decorador @contextmanager, codifica el administrador de contexto de una manera "natural", por lo que es donde usa try..finally en lugar de obtener la excepción como parámetro.

También, cc será el valor que regrese de __enter__(). En su caso, None. La forma en que entiendo el diseño del administrador de contexto es que el valor de retorno es el "contexto". Lo que hace el administrador de contexto es configurar y limpiar contextos en los que sucede algo más. P.ej. una conexión de base de datos creará transacciones, y las operaciones de la base de datos ocurrirán en el alcance de esas transacciones.

Dicho esto, lo anterior solo está ahí para proporcionar la máxima flexibilidad. No tiene nada de malo crear un contexto (que se administre a sí mismo) directamente y devolver self, o incluso no devolver nada si no necesita usar el valor de contexto dentro del with.Puesto que no se utiliza cc en cualquier lugar, sólo podía hacer y no preocuparse por el valor de retorno:

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