jclancy
jclancy

Reputation: 52338

Try...Except on multiple, independent statements, executing as many as possible

I want to execute several functions, gather their exceptions (if there are any), and raise a compound exception, calling as many of the functions as possible without breaking after one exception. For example, say I have

def f():
    do_one()
    do_two()
    do_three()

The do_i functions don't depend on each other's status. The most obvious way to do what I want is this:

def f():
    errors = []
    for do_i in [do_one, do_two, do_three]:
        try:
            do_i()
        except Exception as e:
            errors.append(e)
    if errors:
        raise Exception(';'.join(errors))

or slightly better:

def catch_error(arr, f, *args, **kwargs):
    try:
        return f(*args, **kwargs)
    except Exception as e:
        arr.append(e)
        return None

def f():
    errors = []
    for do_i in [do_one, do_two, do_three]:
        catch_error(errors, do_i)
    if errors:
        raise Exception(';'.join(errors))

but this is still ugly. Is there a Pythonic way to do this that I'm missing, maybe with clever use of a with statement?

Edit: In a dream world Python would have this:

errors = []
awesome_block(errors):
    do_one()
    do_two()
    do_three()
return 'yes!' if not errors else ';'.join(map(str, errors))

Upvotes: 2

Views: 200

Answers (1)

Jeff Tratner
Jeff Tratner

Reputation: 17106

You could rewrite your function into a contextmanager, which does simplify your code a bit. I've maintained your convention of passing a list, though this yields the internal list, so you can use it later.

from contextlib import contextmanager

@contextmanager
def catch_errors(error_list=None):
    error_list = error_list if error_list is not None else []
    try:
        yield error_list
    except Exception as e:
        error_list.append(e)

error_list = []
with catch_errors(error_list):
    raise Exception("First exception")
with catch_errors(error_list):
    raise ValueError("Second exception")

if error_list:
    raise Exception(";".join(map(repr, error_list)))

I think repr is more useful than str here. @contextmanager allows usage in a with statement while you only have to write the function as a generator.

If you don't pass a list to the generator, then you need to keep track of the returned list.

with catch_errors() as errors1:
     raise Exception("First exception")
print errors1 # Exception("First exception",)

Upvotes: 3

Related Questions