0

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

  • 2
    Any code below a `return` is unreachable. The definition of `return` is that that's the last line executed in a function. Putting anything below it will never do anything. Your `attempts = 0` code below `return func(*args, **kwargs)` will never execute. – Random Davis Dec 22 '22 at 16:04
  • 1
    `attempts` goes out of scope as soon as `retry_wrapper` returns anyway, so there's no point in changing its value once `func` succeeds. – chepner Dec 22 '22 at 16:08
  • The next time you call the decorated function, it will get its own fresh `attempts = 0` before the loop is entered. – chepner Dec 22 '22 at 16:09

3 Answers3

0

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

tygzy
  • 698
  • 1
  • 6
  • 23
0

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
Maurice Meyer
  • 17,279
  • 4
  • 30
  • 47
0

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():
    ...
chepner
  • 497,756
  • 71
  • 530
  • 681