¿Cómo evitar intentar atrapar todas las líneas posibles en python?

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 progtwig, 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 

 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)) 

Me encontré con algo similar, e hice una pregunta sobre SO aquí . 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.

 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.

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 "", line 5, in _wrapper File "", line 4, in my_func RuntimeError but_i_wanna_run_this 

A veces, cuando el lenguaje no respalda su elegante forma de express 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 dynamic que admite la statement 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 express (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 / Sagemth 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 eg 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! """