Python While Loop attempts restart to 0 if successful

94 views Asked by At

So I am learning exponential backoff, my code was running too fast and causing api limit errors I created this function and it works. but I want it once it is successful, attempts should be 0 again, right now every time theres an error it justs add one more to attempts and it stops after the 5 try. How can I make attempts =0 again once it is successful ?

Thank you for your help

def retry(func, retries=5): 
    def retry_wrapper(*args, **kwargs):
        attempts = 0
        while attempts < retries:
            try:
                return func(*args, **kwargs) 
                attempts = 0
            except:
                print(f'error {attempts}')
                time.sleep(20)
                attempts += 1
    return retry_wrapper

3

There are 3 answers

4
tygzy On

Ok so there is something you need to know about the return method, once it is run, it doesn't run code after it, so if I have this:

def test():
    return "hi"
    print("Hello")
print(test())

I will only get hi, not hello, because the function stops running when return is called, its similar in a way to break to escape a loop, so you need to put whatever you want to do before the return, always.

try:
    attempts = 0
    return func(*args, **kwargs) 

So your code should look like this

2
Maurice Meyer On

You need to return the result from the decorated function after resetting attempts:

def retry(func, retries=5): 
    def retry_wrapper(*args, **kwargs):
        attempts = 0
        while attempts < retries:
            try:
                res = func(*args, **kwargs)
                # in case func fails, the except block is executed immediately
                attempts = 0
                return res
            except:
                print(f'error {attempts}')
                time.sleep(20)
                attempts += 1
    return retry_wrapper
0
chepner On

You don't need to reset attempts at all; you only need it while the current attempt at calling func is failing. Once it succeeds, retry_wrapper returns, and won't be called again until the next time you explicitly call func, at which point you get 5 fresh attempts again.

You may as well just use a for loop as well, and retries doesn't need to be an argument (though see below if you want to pass a different number).

def retry(func): 
    def retry_wrapper(*args, **kwargs):
        retries = 5
        for attempt in range(retries):
            try:
                return func(*args, **kwargs) 
            except Exception:
                print(f'error {attempt}')
                time.sleep(20)
        raise RuntimeException(f"Failure after {retries} attempts")
    return retry_wrapper


@retry
def some_function(...):
    ...

some_function("foo")  # You get 5 tries to succeed
some_function("bar")  # You get another 5 tries to succeed

To parameterize the decorator on the number of attempts, you need an extra layer of indirection, as the return value of retry(whatever), not retry itself, should receive the function as an argument.

def retry(retries=5):
    def _(func):
        def retry_wrapper(*args, **kwargs):
            for attempt in range(retries):
                try:
                    return func(*args, **kwargs)
                except Exception:
                    print(f'error {attempt}')
                    time.sleep(20)
            raise RuntimeException(f"Failure after {retries} attempts")
        return retry_wrapper
    return _

# 5 tries to succeed
@retry()
def foo1():
    ...

# 10 tries to succeed
@retry(10):
def foo2():
    ...