Question

I have a set of calling functions (bar() in the below example) that do not behave well when exceptions are raised (running tasks in parallel with Celery). I would therefore prefer to catch all exceptions and then return them. My code below demonstrates the desired behavior, however, I find it rather verbose and delicate. Is there a better option available to emulate this behavior? Performance is not an issue.

class V():
    def __init__(self, x):
        self.value = x

class E():
    def __init__(self, e):
        self.error = e

def foo(a,b,c):
    try:
        return V(error_prone_function(a,b,c))
    except Exception as e:
        return E(e)

def bar():
    result = foo(1,2,3)
    if isinstance(result, V):
        do_success(result.value)
    else:
        do_failure(result.error)

EDIT: I would ideally like to have a generalized version of foo(). Something like:

def bar():
    result, error = foo(error_prone_function(a,b,c))
    if result:
        do_success(result)
    else:
        do_failure(error)
Was it helpful?

Solution

You could use a decorator function:

def catchall(f):
    def func(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except Exception as e:
            return e
    return func

Now this can be used on all of your "dodgy" functions:

@catchall
def error_prone_function(...):

and caught elsewhere:

result = error_prone_function(...)
if isinstance(result, Exception):
    # deal with failure
else:
    # success!

However, it is generally better to handle specific errors than the general case "something has gone wrong"; you should really figure out why each routine is failing and install localised error handling to manage it.

OTHER TIPS

you can also return an exception and the real value. Python allows to return multiple values. You can use the first value for the result and the second value for the error. If there is no error, it is a success:

def foo(a,b,c):
    try:
        return error_prone_function(a,b,c), None
    except Exception as e:
        return None, e

def bar():
    result, error = foo(1,2,3)
    # did an error happen
    if error is None:
        do_failure(error)
    else:
        do_success(result)

Here is the decorator for this solution

def catchall(f):
    def func(*args, **kwargs):
        try:
            return f(*args, **kwargs), None
        except Exception as e:
            return None, e
    return func

And then you can call

def bar():
    result, error = catchall(foo)(1,2,3)
    # did an error happen
    if error is None:
        do_failure(error)
    else:
        do_success(result)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top