Search code examples
pythonpython-2.7introspectionpython-decorators

Get python decorator arguments at run time


Is there any kind of way to get the arguments of a decorator function at run time? The project I'm working on is huge and it would be a huge effort to rewrite the affected code. I need a dynamic solution to list all 'categories' assigned to a list of functions. For this I want to avoid super hacky solutions, like regexing through all my modules. Can this be done by inspecting the frames from the call stack?

In our environment the functions are object methods, and we use chained decorators as well. For the ease of understanding I put together this bit of code.

If this is not possible, I can put together another decorator for parts of the project, although it would add much more complexity. But any suggestion to solve my issue is welcome.

def check(*args):
    # do something project relevant, so just return True
    return True

def decorate(*categories):
    def wrap(f):
        def wrap_check_categories(*args, **kwargs):
            if check(*categories):
                return f(*args, **kwargs)
            else:
                raise Exception
        return wrap_check_categories
    return wrap

def get_categories(f):
    '''Returns decorator parameters'''
    # ... do some magic here
    raise NotImplementedError

@decorate('foo', 'bar')
def fancy_func(*args, **kwargs):
    return args, kwargs

def main():
    ## should output ['foo', 'bar']
    print get_categories(fancy_func)

if __name__ == '__main__':
    main()

Solution

  • Modify the decorator to store the categories in an attribute (e.g. _args) of the decorated function:

    def check(*args):
        # do something project relevant, so just return True
        return True
    
    def decorate(*categories):
        def wrap(f):
            def wrap_check_categories(*args, **kwargs):
                if check(*categories):
                    return f(*args, **kwargs)
                else:
                    raise Exception
            wrap_check_categories._args = categories    # <-- store the categories
            return wrap_check_categories
        return wrap
    
    def get_categories(f):
        '''Returns decorator parameters'''
        # ... do some magic here
        return f._args
    
    @decorate('foo', 'bar')
    def fancy_func(*args, **kwargs):
        return args, kwargs
    
    def main():
        ## should output ['foo', 'bar']
        print get_categories(fancy_func)
    
    if __name__ == '__main__':
        main()
    

    yields

    ('foo', 'bar')