bsperl
bsperl

Reputation: 1

Exception handling - sleep then retry the same line that produces error

suppose I have an exception that is (unpredictably/inconsistently) raised at different lines in the script every time I run it. How can I handle this exception in such a way that (1) forces the script to sleep for a few seconds following the error then (2) tries to re-execute the same line of code that caused the error in hopes of a successful result the 2nd time around (or 3rd, or 4th ...), AND (3) does not re-execute all of the lines leading up to that point in the script? In other words, I want it to "goto" the previous line and try again until it succeeds, albeit with a cap on the number of attempts to avoid an infinite loop.

The code below is conceptual example of my problem. The desired result would print:

A B C D E

but instead prints something like:

A A B A B A A A A B A A A B C D E

import time, random

success, attempts = False, 0

while success == False and attempts <= 20:
    try:

        x, y = 1, random.choice([0,1])
        x/y
        print "A"

        x, y = 1, random.choice([0,1])
        x/y
        print "B"

        x, y = 1, random.choice([0,1])
        x/y
        print "C"

        x, y = 1, random.choice([0,1])
        x/y
        print "D"

        x, y = 1, random.choice([0,1])
        x/y
        print "E"

        success = True
    except ZeroDivisionError:
        attempts += 1
        time.sleep(3)

Thanks in advance.

Upvotes: 0

Views: 372

Answers (1)

chepner
chepner

Reputation: 532023

You can use conditionals and an explicitly tracked to-do list to fake a set of goto's. Each time a task succeeds, remove it from the to-do list so that you won't try it again.

import time, random

attempts = 0
# Reverse order because it's more efficient to remove an item
# from the end of a list than the beginning. Use a deque
# if this bothers you.
to_do = ["E", "D", "C", "B", "A"]
while to_do and attempts <= 20:
    try:
        if to_do[-1] == "A":
            x, y = 1, random.choice([0,1])
            x/y
            print "A"
            to_do.pop()
        elif to_do[-1] == "B": 
            x, y = 1, random.choice([0,1])
            x/y
            print "B"
            to_do.pop()
        elif to_do[-1] == "C":
            x, y = 1, random.choice([0,1])
            x/y
            print "C"
            to_do.pop()
        elif to_do[-1] == "D":
            x, y = 1, random.choice([0,1])
            x/y
            print "D"
            to_do.pop()
        elif to_do[-1] == "E":
            x, y = 1, random.choice([0,1])
            x/y
            print "E"
            to_do.pop()
    except ZeroDivisionError:
        attempts += 1
        time.sleep(3)

However, this can be cleaned up significantly by defining a separate function for each task, and using the presence of the function itself on your failures list to indicate it still needs to be run.

to_do = [task_e, task_d, task_c, task_b, task_a]
while attempts <= 20:
    while to_do:
        try:
            to_do[-1]()
        except ZeroDivisionError:
            attempts += 1
            time.sleep(3)
        else:
            to_do.pop()

Upvotes: 1

Related Questions