cascading python executions only if return code is 0

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Dec 23 01:25:00 CET 2013


On Sun, 22 Dec 2013 15:37:04 -0300, Frank Cui wrote:

> hey guys,
> I have a requirement where I need to sequentially execute a bunch of
> executions, each execution has a return code. the followed executions
> should only be executed if the return code is 0. is there a cleaner or
> more pythonic way to do this other than the following ? 
> if a() == 0:
>     if b() == 0:
>         c()

I don't believe there is a clean way to deal with error return codes in 
*any* language, but certainly not Python.

If you only have a few such functions, you can mis-use boolean operators 
to get the result you want, at the cost of misleading code:

a() == 0 and b() == 0 and c()

But that's pretty horrible code, because it looks like you're testing a 
condition when you're really trying to run a, b, c etc. for their side-
effects. Code that relies on side-effects is usually a sign of poor 
design.

A better alternative is to modify the functions so that instead of 
returning 0 on failure and (I'm guessing here) None on success, they 
raise an exception instead. Instead of:

def a():
    do_this()
    do_that()
    if condition:
        return 0
    do_something_else()


you re-write it as:

def a():
    do_this()
    do_that()
    if condition:
        raise SomeError("something broke")
    do_something_else()


Then you can do this:


try:
    a()
    b()
    c()
except SomeError:
    handle_error()


What if you can't edit all the various a, b, c functions because other 
parts of your code rely on them returning an error result? That's easy, 
you just need an adaptor:

import functools

def adapt(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        result = func(*args, **kwargs)
        if result == 0:
            raise SomeError("some message")
    return inner


try:
    adapt(a)()
    adapt(b)()
    adapt(c)()
except SomeError:
    handle_error()


Another option really only applies if all the functions use the same set 
of arguments.

def chain(list_of_functions, *args, **kwargs):
    for func in list_of_functions:
        result = func(*args, **kwargs)
        if result == 0:
            break


-- 
Steven



More information about the Python-list mailing list