Question

I have a trouble to find the "pythonic" way to do this: I need to catch different blocks of code with the same try-except pattern. The blocks to catch are different from each other. Currently I am repeating the same try-except pattern in several points of the code with a long list of exceptions.

try:
    block to catch
except E1, e:
    log and do something
except E2, e:
    log and do something
...
except Exception, e:
    log and do something

There is a good way to solve this using the with statement and a context manager decorator:

from contextlib import contextmanager

@contextmanager
def error_handler():
    try:
        yield
    except E1, e:
        log and do something
    ...
    except Exception, e:
        log and do something


...
with error_handler():
    block to catch
...

But, what happens if I need to know whether or not there has been an exception in the block? i.e. Is there any alternative to do something like the previous with block for try-except-else?

A use case example:

for var in vars:
    try:
        block to catch
    except E1, e:
        log and do something
        # continue to the next loop iteration
    except E2, e:
        log and do something
        # continue to the next loop iteration
    ...
    except Exception, e:
        log and do something
        # continue to the next loop iteration
    else:
        do something else

Could I do something like that in a pythonic way to avoid repeat the same try-except pattern again and again?

Was it helpful?

Solution

I can see that you already got an answer, but building on what you already have, you could yield back an object indicating the error status, and use that for checking in your loop.

Before using this style though, you should really consider if hiding the error handling/logging in this kind of structure is really something you want to do, being "Pythonic" is usually leaning more towards being explicit instead of hiding the details.

from contextlib import contextmanager

@contextmanager
def error_handler():
    error = True
    try:
        class Internal:
            def caughtError(self): return error
        yield Internal()
    except Exception as e:
        print("Logging#1")
    except BaseException as e:
        print("Logging#2")
    else:
        error = False

with error_handler() as e:
    print("here")
#    raise Exception("hopp")

print(e.caughtError())         # True if an error was logged, False otherwise

OTHER TIPS

Can't see a way to give the no exception information back to the caller. You could only put the error handling in a separate function

def error_handler(exception):
    if isinstance(exception, E1):
        log and do something
    elif isinstance(exception, E2):
    ...
    else:
        log and do something

try:
    block to catch
except Exception, e:
    error_handler(e)
else:
    do something else

Fundamentally you want to let your error propagate, so instead of, say:

def foo():
    try:
        something = xxx
    except ValueError as e:
        logging.warn("oh noes %s", e)
        return 42
    except IndexError as e:
        logging.warn("on no, not again %s", e)
        return 43

    go.on(something=something)

How about this:

def foo():
    try:
        something = xxx
    except Exception:
        logging.exception("oh noes")
        return

    go.on(something=something)

And then perhaps simplify further with:

def foo():
    go.on(something=xxx)

And let caller handle the error.

This approach is typically called Crash Early

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top