views:

3934

answers:

6

Sometimes I find myself in the situation where I want to execute several sequential commands like such:

try:
    foo(a, b)
except Exception, e:
    baz(e)
try:
    bar(c, d)
except Exception, e:
    baz(e)
...

This same pattern occurs when exceptions simply need to be ignored.

This feels redundant and the excessive syntax causes it to be surprisingly difficult to follow when reading code.

In C, I would have solved this type of problem easily with a macro, but unfortunately, this cannot be done in straight python.

Question: How can I best reduce the code footprint and increase code readability when coming across this pattern?

+2  A: 

If they're simple one-line commands, you can wrap them in lambdas:

for cmd in [
    (lambda: foo (a, b)),
    (lambda: bar (c, d)),
]:
    try:
        cmd ()
    except StandardError, e:
        baz (e)

You could wrap that whole thing up in a function, so it looked like this:

ignore_errors (baz, [
    (lambda: foo (a, b)),
    (lambda: bar (c, d)),
])
John Millikin
+1  A: 

The best approach I have found, is to define a function like such:

def handle_exception(function, reaction, *args, **kwargs):
    try:
        result = function(*args, **kwargs)
    except Exception, e:
        result = reaction(e)
    return result

But that just doesn't feel or look right in practice:

handle_exception(foo, baz, a, b)
handle_exception(bar, baz, c, d)
Sufian
You can simplify that function by just returning instead of assigning to "result".
John Millikin
A: 

In your specific case, you can do this:

try:
    foo(a, b)
    bar(c, d)
except Exception, e:
    baz(e)

Or, you can catch the exception one step above:

try:
    foo_bar() # This function can throw at several places
except Exception, e:
    baz(e)
Martin Cote
If foo() raised an exception, bar() would not be executed. That is not the desired behavior.
Sufian
+16  A: 

You could use the with statement if you have python 2.5

from __future__ import with_statement
import contextlib

@contextlib.contextmanager
def handler():
    try:
        yield
    except Exception, e:
        baz(e)

Your example now becomes:

with handler():
    foo(a, b)
with handler():
    bar(c, d)
Ryan
+1  A: 

You could try something like this. This is vaguely C macro-like.

class TryOrBaz( object ):
    def __init__( self, that ):
        self.that= that
    def __call__( self, *args ):
        try:
            return self.that( *args )
        except Exception, e:
            baz( e )

TryOrBaz( foo )( a, b )
TryOrBaz( bar )( c, d )
S.Lott
Your spacing is horrible. See "Whitespace in Expressions and Statements" http://www.python.org/dev/peps/pep-0008/
J.F. Sebastian
Not "horrible". Idiomatic and atypical. And a 30-year habit starting from Pascal and C. Not changing any time soon to remove spaces from close to ()'s. Need the spaces for aging infrastructure (eyes, mostly)
S.Lott
+1  A: 

If this is always, always the behaviour you want when a particular function raises an exception, you could use a decorator:

def handle_exception(handler):
    def decorate(func):
        def call_function(*args, **kwargs):
            try:
                func(*args, **kwargs)
            except Exception, e:
                handler(e)
        return call_function
    return decorate

def baz(e):
    print(e)

@handle_exception(baz)
def foo(a, b):
    return a + b

@handle_exception(baz)
def bar(c, d):
    return c.index(d)

Usage:

>>> foo(1, '2')
unsupported operand type(s) for +: 'int' and 'str'
>>> bar('steve', 'cheese')
substring not found
insin