Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cleaning up nested Try/Excepts

Tags:

python

I've just written a chunk of code that strikes me as being far more nested than is optimal. I'd like advice on how to improve the style of this, particularly so that it conforms more with "Flat is better than nested."

for app in apps:
    if app.split('.', 1)[0] == 'zc': #only look for cron in zc apps
        try:
            a = app + '.cron'
            __import__(a)
            m = sys.modules[a]

            try:
                min = m.cron_minute()
                for job in min:
                    k.add_interval_task(job[0], 'minute task', r(M_LB, M_UB),
                                        60*job[1], 
                                        kronos.method.threaded, (), ())
            except AttributeError: #no minute tasks
                pass

            try:
                hour = m.cron_hour()
                for job in hour:
                    k.add_daytime_task(job[0], 'day task', range(1, 8), None,
                                       (job[1], r(H_LB, H_UB)), 
                                       kronos.method.threaded, (), ())
            except AttributeError: #no hour tasks
                pass

        except ImportError: #no cron jobs for this module
            pass

Edit: Combining the suggestions from below, here's my rewritten form.

for app in apps:
    if app.split('.', 1)[0] != 'zc': #only look for cron in zc apps
        continue

    try:
        a = app + '.cron'
        __import__(a)
    except ImportError: #no cron jobs for this module, continue to next one
        continue

    m = sys.modules[a]
    if hasattr(m, 'cron_minute'):
        min = m.cron_minute()
        for job in min:
            k.add_interval_task(job[0], 'minute task', r(M_LB, M_UB),
                                60*job[1], 
                                kronos.method.threaded, (), ())

    if hasattr(m, 'cron_hour'):
        hour = m.cron_hour()
        for job in hour:
            k.add_daytime_task(job[0], 'day task', range(1, 8), None,
                               (job[1], r(H_LB, H_UB)), 
                               kronos.method.threaded, (), ())
like image 591
Paul McMillan Avatar asked Jan 23 '23 10:01

Paul McMillan


1 Answers

The main problem is that your try clauses are too broad, particularly the outermost one: with that kind of habit, you WILL sooner or later run into a mysterious bug because one of your try/except has accidentally hidden an unexpected exception "bubbling up" from some other function you're calling.

So I'd suggest, instead:

for app in apps:
    if app.split('.', 1)[0] != 'zc': #only look for cron in zc apps
        continue

    try:
        a = app + '.cron'
        __import__(a)
    except ImportError: #no cron jobs for this module
        continue

    # etc etc

As an aside, I'm also applying "flat is better than nested" in another way (not dependent on any try/except) which is "if I have nothing more to do on this leg of the loop, continue [i.e. move on to the next leg of the loop] instead of "if I have something to do:" followed by a substantial amount of nested code. I've always preferred this style (of if/continue or if/return) to nested if's in languages that supply functionality such as continue (essentially all modern ones, since C has it;-).

But this is a simple "flat vs nested" style preference, and the meat of the issue is: keep your try clauses small! Worst case, when you just can't simply continue or return in the except clause, you can use try/except/else: put in the try clause only what absolutely MUST be there -- the tiny piece of code that's likely and expected to raise -- and put the rest of the following code (the part that's NOT supposed nor expected to raise) in the else clause. This doesn't change the nesting, but DOES make a huge difference in lowering the risk of accidentally hiding exceptions that are NOT expected!

like image 178
Alex Martelli Avatar answered Jan 25 '23 23:01

Alex Martelli