eyed3 - ¿Cómo evitar intentar atrapar todas las líneas posibles en python?
exception exception-handling (6)
Tengo muchas líneas seguidas que pueden lanzar una excepción, pero no importa qué, todavía debe continuar la siguiente línea. ¿Cómo hacer esto sin intentar individualmente capturar cada una de las afirmaciones que pueden generar una excepción?
try:
this_may_cause_an_exception()
but_I_still_wanna_run_this()
and_this()
and_also_this()
except Exception, e:
logging.exception(''An error maybe occured in one of first occuring functions causing the others not to be executed. Locals: {locals}''.format(locals=locals()))
Veamos el código anterior, todas las funciones pueden generar excepciones, pero aún así deberían ejecutar las siguientes funciones, sin importar si lanzaron una excepción o no. ¿Hay una buena manera de hacer eso?
No quiero hacer esto:
try:
this_may_cause_an_exception()
except:
pass
try:
but_I_still_wanna_run_this()
except:
pass
try:
and_this()
except:
pass
try:
and_also_this()
except:
pass
Creo que el código aún debe continuar ejecutándose después de una excepción solo si la excepción es crítica (la computadora se quemará o todo el sistema se desordenará, debería detener todo el programa, pero para muchas cosas pequeñas también se lanzan excepciones como la conexión fallado, etc.) Normalmente no tengo ningún problema con el manejo de excepciones, pero en este caso estoy usando una biblioteca de terceros que fácilmente lanza excepciones para cosas pequeñas.
Después de ver la respuesta de m4spy, pensé que no sería posible tener un decorador que permita que todas las líneas de la función se ejecuten, incluso si una de ellas genera una excepción.
Algo como esto sería genial:
def silent_log_exceptions(func):
@wraps(func)
def _wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception:
logging.exception(''...'')
some_special_python_keyword # which causes it to continue executing the next line
return _wrapper
O algo como esto:
def silent_log_exceptions(func):
@wraps(func)
def _wrapper(*args, **kwargs):
for line in func(*args, **kwargs):
try:
exec line
except Exception:
logging.exception(''...'')
return _wrapper
@silent_log_exceptions
def save_tweets():
a = requests.get(''http://twitter.com)
x = parse(a)
bla = x * x
A veces, cuando el lenguaje no respalda su elegante forma de expresar una idea porque el desarrollo del lenguaje falló literalmente en las últimas décadas, solo puede confiar en el hecho de que Python sigue siendo un lenguaje dinámico que admite la declaración exec, lo que hace posible lo siguiente:
code="""
for i in range(Square_Size):
Square[i,i] @= 1
Square[i+1,i] @= 2
@dowhatever()
"""
Este nuevo operador hace que el código sea más sintético y elegante, ya que no es necesario que especifique estados-if adicionales que garanticen que el índice permanezca enlazado o que la función tenga éxito, lo que es totalmente irrelevante para lo que queremos expresar (simplemente no debería detener) aquí (nota: aunque la indexación segura sería posible mediante la creación de una clase basada en la clase de lista, este operador funciona siempre que debería haber una captura de prueba), en Lisp sería fácil definirla de una manera Lispy, pero Las costuras son imposibles de definir de forma elegante en Python, pero aún así, aquí está el pequeño preparser que lo hará posible: exec "/n".join([o+"try: "+z.replace("@","")+"/n"+o+"except: pass" if "@" in z else z for z in code.split("/n") for o in ["".join([h for h in z if h==" "])]]) #new <- hackish operator which wraps try catch into line
El resultado, suponiendo que Square era 4x4 y contenía solo ceros:
[1 0 0 0]
[2 1 0 0]
[0 2 1 0]
[0 0 2 1]
Relevante: Sage / Sagemath CAS utiliza una función de preparsa que transforma el código antes de que llegue al intérprete de Python. Un parche de mono para esa función sería:
def new_preparse(code,*args, **kwargs):
code="/n".join([o+"try: "+z.replace("@","")+"/n"+o+"except: pass" if "@" in z else z for z in code.split("/n") for o in ["".join([h for h in z if h==" "])]])
return preparse(code)
sage.misc.preparser.preparse=new_preparse
Aparte de las respuestas proporcionadas, creo que vale la pena tener en cuenta que se han propuesto declaraciones de try-except
una línea; consulte el PEP 463 relacionado con el desafortunado aviso de rechazo:
""" I want to reject this PEP. I think the proposed syntax is acceptable given the
desired semantics, although it''s still a bit jarring. It''s probably no worse than the
colon used with lambda (which echoes the colon used in a def just like the colon here
echoes the one in a try/except) and definitely better than the alternatives listed.
But the thing I can''t get behind are the motivation and rationale. I don''t think that
e.g. dict.get() would be unnecessary once we have except expressions, and I disagree
with the position that EAFP is better than LBYL, or "generally recommended" by Python.
(Where do you get that? From the same sources that are so obsessed with DRY they''d rather
introduce a higher-order-function than repeat one line of code? :-)
This is probably the most you can get out of me as far as a pronouncement. Given that
the language summit is coming up I''d be happy to dive deeper in my reasons for rejecting
it there (if there''s demand).
I do think that (apart from never explaining those dreadful acronyms :-) this was a
well-written and well-researched PEP, and I think you''ve done a great job moderating the
discussion, collecting objections, reviewing alternatives, and everything else that is
required to turn a heated debate into a PEP. Well done Chris (and everyone who
helped), and good luck with your next PEP! """
Me encontré con algo similar, e hice una pregunta sobre SO here . La respuesta aceptada maneja el registro y la observación solo para una excepción específica. Terminé con una versión modificada:
class Suppressor:
def __init__(self, exception_type, l=None):
self._exception_type = exception_type
self.logger = logging.getLogger(''Suppressor'')
if l:
self.l = l
else:
self.l = {}
def __call__(self, expression):
try:
exec expression in self.l
except self._exception_type as e:
self.logger.debug(''Suppressor: suppressed exception %s with content /'%s/''' % (type(self._exception_type), e))
Utilizable como tal:
s = Suppressor(yourError, locals())
s(cmdString)
Por lo tanto, puede configurar una lista de comandos y usar el map
con el supresor para ejecutarlos todos.
Puedes manejar esa tarea con un decorador:
import logging
from functools import wraps
def log_ex(func):
@wraps(func)
def _wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception:
logging.exception(''...'')
return _wrapper
@log_ex
def this_may_cause_an_exception():
print ''this_may_cause_an_exception''
raise RuntimeError()
@log_ex
def but_i_wanna_run_this():
print ''but_i_wanna_run_this''
def test():
this_may_cause_an_exception()
but_i_wanna_run_this()
Llamar a la función de prueba se verá como (lo que mostrará que ambas funciones se ejecutaron):
>>> test()
this_may_cause_an_exception
ERROR:root:...
Traceback (most recent call last):
File "<stdin>", line 5, in _wrapper
File "<stdin>", line 4, in my_func
RuntimeError
but_i_wanna_run_this
for func in [this_may_cause_an_exception,
but_I_still_wanna_run_this,
and_this,
and_also_this]:
try:
func()
except:
pass
Hay dos cosas que notar aquí:
- Todas las acciones que desea realizar deben estar representadas por callables con la misma firma (en el ejemplo, callables que no tienen argumentos). Si aún no lo están, envuélvalos en funciones pequeñas, expresiones
lambda
, clases que se pueden llamar, etc. - Bare
except
cláusulas son una mala idea, pero probablemente ya lo sabías.
Un enfoque alternativo, que es más flexible, es usar una función de orden superior como
def logging_exceptions(f, *args, **kwargs):
try:
f(*args, **kwargs)
except Exception as e:
print("Houston, we have a problem: {0}".format(e))
try:
this_may_cause_an_exception()
except:
logging.exception(''An error occured'')
finally:
but_I_still_wanna_run_this()
and_this()
and_also_this()
Puedes usar el bloque finally de manejo de excepciones. Sin embargo, en realidad es para el código de limpieza.
EDIT: Veo que dijo que todas las funciones pueden generar excepciones, en cuyo caso la respuesta de larsmans es la más limpia que se me ocurre para detectar una excepción en cada llamada de función.