Skip to content
Advertisement

How to avoid name collisions in python decorators functions

I would like to write a python decorator so that a function raising an exception will be run again until either it succeeds, or it reaches the maximum number of attempts before giving up.

Like so :

def tryagain(func):
    def retrier(*args,**kwargs,attempts=MAXIMUM):
        try:
            return func(*args,**kwargs)
        except Exception as e:
            if numberofattempts > 0:
                logging.error("Failed. Trying again")
                return retrier(*args,**kwargs,attempts=attempts-1)
            else:
                logging.error("Tried %d times and failed, giving up" % MAXIMUM)
                raise e
    return retrier

My problem is that I want a guarantee that no matter what names the kwargs contain, there cannot be a collision with the name used to denote the number of attempts made.

however this does not work when the function itself takes attempts as a keyword argument

@tryagain
def other(a,b,attempts=c):
    ...
    raise Exception

other(x,y,attempts=z)

In this example,if other is run, it will run z times and not MAXIMUM times (note that for this bug to happen, the keyword argument must be explicitly used in the call !).

Advertisement

Answer

You can specify decorator parameter, something along the lines of this:

import logging

MAXIMUM = 5

def tryagain(attempts=MAXIMUM):
    def __retrier(func):
        def retrier(*args,**kwargs):
            nonlocal attempts
            while True:
                try:
                    return func(*args,**kwargs)
                except Exception as e:
                    attempts -= 1
                    if attempts > 0:
                        print('Failed, attempts left=', attempts)
                        continue
                    else:
                        print('Giving up')
                        raise
        return retrier
    return __retrier


@tryagain(5)                              # <-- this specifies number of attempts
def fun(attempts='This is my parameter'): # <-- here the function specifies its own `attempts` parameter, unrelated to decorator
    raise Exception(attempts)

fun()
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement