parser - web scraping python
Varios `con`s en` try`s (3)
Tengo varios archivos posibles que pueden contener mis datos; se pueden comprimir de diferentes maneras, así que para abrirlos necesito usar file()
, gzip.GzipFile()
y otros que también devuelvan un objeto de archivo (que admite la interfaz with
).
Quiero probar cada uno de ellos hasta que uno tenga éxito en la apertura, así 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 que tenga docenas de posibles variantes de compresión. (El anidamiento se hará cada vez más profundo, el código siempre se parecerá mucho).
¿Hay alguna manera más agradable de deletrear esto?
EDITAR: Si es posible, me gustaría tener el process(f)
fuera de la prueba / excepto también para evitar la captura accidental de excepciones planteadas en el process(f)
.
¿Funcionaría esto?
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
Escribiría un administrador 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 solo una función que devuelve un administrador 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"
Sí, puedes poner todas tus variantes a través de una lista y probarlas hasta que una de ellas funcione, anulando así tu 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 bien, para reducir la cantidad de código, puede crear una fábrica process_func, ya que todos 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),
]