Generic Exception Handling in Python the "Right Way"

Question:

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?

Asked By: Sufian

||

Answers:

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)),
])
Answered By: John Millikin

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)
Answered By: Sufian

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)
Answered By: Martin Cote

You could use the with statement if you have python 2.5 or above:

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)
Answered By: Ryan

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 )
Answered By: S.Lott

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
Answered By: Jonny Buchanan
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.