2012-09-24 22 views
17

Tengo varios archivos posibles que podrían contener mis datos; se pueden comprimir de diferentes formas, así que para abrirlas necesito usar file(), gzip.GzipFile() y otras que también devuelvan un objeto de archivo (compatible con la interfaz with).Varios `con`s en` try`s

quiero probar cada uno de ellos hasta que uno tiene éxito en la apertura, por lo que podría hacer algo como

try: 
    with gzip.GzipFile(fn + '.gz') as f: 
    result = process(f) 
except (IOError, MaybeSomeGzipExceptions): 
    try: 
    with xCompressLib.xCompressFile(fn + '.x') as f: 
     result = process(f) 
    except (IOError, MaybeSomeXCompressExceptions): 
    try: 
     with file(fn) as f: 
     result = process(f) 
    except IOError: 
     result = "some default value" 

lo que obviamente no es factible en caso de tener docenas de posibles variantes de compresión. (El anidamiento se hará más y más profundo, el código siempre se parece mucho).

¿Hay alguna forma más sencilla de deletrear esto?

EDITAR: Si es posible me gustaría tener el process(f) fuera del intento/excepto también para evitar la captura accidental de excepciones planteadas en el process(f).

Respuesta

7

me gustaría escribir un gestor de contexto personalizado:

from contextlib import contextmanager 

filetypes = [('.gz', gzip.GzipFile, (IOError, MaybeSomeGzipExceptions)), 
      ('.x', xCompressLib.xCompressFile, (IOError, MaybeSomeXCompressExceptions))] 

@contextmanager 
def open_compressed(fn): 
    f = None 
    try: 
     for ext, cls, exs in filetypes: 
      try: 
       f = cls(fn + ext) 
      except exs: 
       pass 
      else: 
       break 
     yield f 
    finally: 
     if f is not None: 
      f.close() 

with open_compressed(fn) as f: 
    result = "some default value" if f is None else process(f) 

O, posiblemente, sólo una función que devuelve un gestor de contexto:

filetypes = [('.gz', gzip.GzipFile, (IOError, MaybeSomeGzipExceptions)), 
      ('.x', xCompressLib.xCompressFile, (IOError, MaybeSomeXCompressExceptions))] 

class UnknownCompressionFormat(Exception): 
    pass 

def open_compressed(fn): 
    for ext, cls, exs in filetypes: 
     try: 
      return cls(fn + ext) 
     except exs: 
      pass 
    raise UnknownCompressionFormat 

try: 
    with open_compressed(fn) as f: 
     result = process(f) 
except UnknownCompressionFormat: 
    result = "some default value" 
+0

Creo que me gusta ese enfoque incluso más que el otro. – Alfe

+2

Agradable. Me gusta especialmente el segundo enfoque. Sugeriría que ambos enfoques arrojen una excepción para UnknownCompressionFormat. –

9

Sí, se podría poner todas sus variantes a través de una lista y probarlos hasta que uno de ellos funciona, por lo tanto no-anidación su código:

def process_gzip(fn): 
    with gzip.GzipFile(fn + '.gz') as f: 
     return process(f) 

def process_xlib(fn): 
    with xCompressLib.xCompressFile(fn + '.x') as f: 
     return process(f) 

def process_builtin(fn): 
    with file(fn) as f: 
     return process(f) 

process_funcs = [process_gzip, process_xlib, process_builtin] 

#processing code: 

for process_f in process_funcs: 
    try: 
     result = process_f(fn) 
     break 
    except IOError: 
     #error reading the file, keep going 
     continue 
    except: 
     #processing error, re-raise the exception 
     raise 

O, para reducir la cantidad de código que podría hacer un process_func fábrica, ya que todos ellos tienen la misma forma:

def make_process_func(constructor, filename_transform): 
    with constructor(filename_transform) as f: 
     return process(f) 

process_funcs = [ 
    make_process_func(gzip.GzipFile, lambda fn: fn + '.gz'), 
    make_process_func(xCompressLib.xCompressFile, lambda fn: fn + '.x'), 
    make_process_func(file, lambda fn: fn), 
] 
+0

tengo una edición a mi pregunta: me Me gustaría tener el 'proceso (f)' fuera del try/excepto si es posible. – Alfe

+1

simplemente abriendo el archivo podría no causar un IOError. leer el archivo también podría hacerlo. así que querrá leer todos los datos y luego procesarlos si quiere separar IOError del error de procesamiento, ¿no? – Claudiu

+1

Lo único que agregaría aquí es que probablemente deba tener tuplas disponibles que contengan las diversas excepciones aceptables. p.ej. 'try: ; excepto passable_exceptions: pase' – mgilson

5

que este trabajo:

extensions = [('.gz', gzip.GzipFile, (IOError, MaybeSomeGzipExceptions)), 
       ('.x', xCompressLib.xCompressFile, (IOError, MaybeSomeXCompressExceptions))] # and other such entries 
processed = False 
for ext, (compressor, errors) in extensions.iteritems(): 
    try: 
     with compressor(fn+ext) as f: 
      try: 
       result = process(f) 
       processed = True 
       break 
      except: 
       raise 
    except errors: 
     pass 
if not processed: 
    result = "some default value" 

espero que ayude

+0

Una vez más, tengo un EDITAR a mi pregunta: ¿es posible obtener el 'proceso (f)' fuera de la try/catch (que solo debería capturar excepciones en el 'con 'cosas de todos modos)? – Alfe

+0

Esto es lo que yo también haría, pero es posible que el orden sea importante. – mgilson

+0

El pedido realmente importa, pero usar una lista de tuplas en lugar de un dict no es ningún problema. – Alfe

Cuestiones relacionadas