Search code examples
pythonpython-3.xdecoratorpython-decorators

Python Decorator with Arguments only called once


Please consider the following simplified example:

permitted = True
class is_allowed(object):
    def __init__(self, some_arg):
        # this is actually needed in the complete code
        self.some_arg = some_arg

    def __call__(self, f):
        if permitted == False:
            raise Exception("not authenticated to do that")
        def wrapped_f(*args, **kwargs):
            f(*args, **kwargs)
        return wrapped_f

@is_allowed("blah")
def print_hi():
    print("hi")

print_hi()
permitted = False
print_hi()

I guess the problem is that the decorator is only called once when the function print_hi() is defined. Because of that the change of the global variable has no effect. Is there any way to circumwent this behaviour?


Solution

  • Move the check inside of the wrapped_f

    def __call__(self, f):
        def wrapped_f(*args, **kwargs):
            if not permitted:
                raise Exception("not authenticated to do that")
            f(*args, **kwargs)
        return wrapped_f
    

    Outside of the wrapped_f, it is checked at the creation of the function. Inside, it becomes part of the body of the new callable, which means it will be checked each time there is a call.

    You want to realize that wrapped_f will be called instead of print_hi, so you want any behavior that should be included in the function to go inside of that.