22

I got many lines in a row which may throw an exception, but no matter what, it should still continue the next line. How to do this without individually try catching every single statement that may throw an exception?

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

Let's see above code, all functions may throw exceptions, but it should still execute the next functions no matter if it threw an exception or not. Is there a nice way of doing that?

I dont wanna do this:

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

I think code should still continue to run after an exception only if the exception is critical (The computer will burn or the whole system will get messed up, it should stop the whole program, but for many small things also exceptions are thrown such as connection failed etc.) I normally don't have any problems with exception handling, but in this case I'm using a 3rd party library which easily throws exceptions for small things.

After looking at m4spy's answer, i thought wouldn't it be possible, to have a decorator which will let every line in the function execute even if one of them raises an exception.

Something like this would be cool:

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

Or something like this:

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
Sam Stoelinga
  • 4,881
  • 7
  • 39
  • 54
  • 4
    Neither of those styles of coding feel terribly pythonic...I'm not really sure what to suggest, but I look forward to the answer. – Chris Pfohl Jun 05 '12 at 14:09

6 Answers6

27
for func in [this_may_cause_an_exception,
             but_I_still_wanna_run_this,
             and_this,
             and_also_this]:
    try:
        func()
    except:
        pass

There are two things to notice here:

  • All actions you want to perform have to represented by callables with the same signature (in the example, callables that take no arguments). If they aren't already, wrap them in small functions, lambda expressions, callable classes, etc.
  • Bare except clauses are a bad idea, but you probably already knew that.

An alternative approach, that is more flexible, is to use a higher-order function like

def logging_exceptions(f, *args, **kwargs):
    try:
        f(*args, **kwargs)
    except Exception as e:
        print("Houston, we have a problem: {0}".format(e))
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • Yea I knew, thats why I put the first one with logging of locals in the question. It's a new thing I got used to recently to log my locals helps a lot with debugging stuff on production servers. I really like the way you make a list of functions, although in my question it's all functions in my real life example most of them are statements and saving variables also. But could work a way around that though. – Sam Stoelinga Jun 05 '12 at 14:13
3

I ran into something similar, and asked a question on SO here. The accepted answer handles logging, and watching for only a specific exception. I ended up with a modified version:

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

Usable like so:

s = Suppressor(yourError, locals()) 
s(cmdString)

So you could set up a list of commands and use map with the suppressor to run across all of them.

Community
  • 1
  • 1
Spencer Rathbun
  • 14,510
  • 6
  • 54
  • 73
  • 3
    Never set a default parameter value to a mutable like `List`, `Dictionary` or (in some conditions) `object`. Because they are evaluated only once and will cause serious memory leaks and logical errors. http://docs.python.org/tutorial/controlflow.html#default-argument-values – Mp0int Jun 05 '12 at 14:27
0

You can handle such a task with a decorator:

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

Calling the test function will look like (which will show that both functions were executed):

>>> 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
schlamar
  • 9,238
  • 3
  • 38
  • 76
  • What if a line in the function raises an exception, i dont think it would execute the next line right? But instead get out of the function and log the exception? – Sam Stoelinga Jun 06 '12 at 11:37
  • @SamStoelinga No, but the next function will be called. I updated the answer above so it should be more clear. It is a similar approach than the accepted answer but you don't have to create the list and your functions do not need to have a fixed signature. – schlamar Jun 06 '12 at 12:13
  • Ah yea that was stupid of me ;) also a nice workaround but would still cause me lots of code, I think the best solution would be to have a decorator, which will let the whole function execute no matter if any line of the function raises an error, not sure if this is possible though in python. – Sam Stoelinga Jun 06 '12 at 12:25
0

Sometimes, when language misses to support your elegant way of expressing an idea because language development literally failed the last decades, you can only rely on the fact that Python is still a dynamical language which supports the exec statement, which makes the following possible:

code="""
for i in range(Square_Size):
    Square[i,i] @= 1
    Square[i+1,i] @= 2
    @dowhatever()
"""

This new operator makes code more pythonic and elegant since you don't need to specify additional if-statemens that guarantee that the index stays in bound or the function does succeed which is totally irrelevant to what we want to express (it just shouldn't stop) here (note: while safe indexing would be possible by creating a class based on the list class, this operator works whenever there should be a try catch) , in Lisp it would be easy to define it in a Lispy way, but it seams to be impossible to define it in an elegant way in Python, but still, here is the little preparser which will make it possible: 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

The result, assuming that Square was 4x4 and contained only zeros:

[1 0 0 0]
[2 1 0 0]
[0 2 1 0]
[0 0 2 1]

Relevant: The Sage / Sagemath CAS uses a preparse-function which transforms code before it reaches the Python interpreter. A monkey-patch for that function would be:

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
0

Apart from the answers provided, I think its worth to note that one-line try-except statements have been proposed - see the related PEP 463 with the unfortunate rejection notice:

""" 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! """
Roelant
  • 4,508
  • 1
  • 32
  • 62
-3
try:
    this_may_cause_an_exception()
except:
    logging.exception('An error occured')
finally:
    but_I_still_wanna_run_this()
    and_this()
    and_also_this()

You can use the finally block of exception handling. It is actually meant for cleanup code though.

EDIT: I see you said all of the functions can throw exceptions, in which case larsmans' answer is about the cleanest I can think of to catch exception for each function call.

Christian Witts
  • 11,375
  • 1
  • 33
  • 46