Search code examples
pythonpython-decoratorsintrospection

detecting decorated function in module


I have a source for a python module, and I want to find a specific function (function, not a class, not a method in a class etc.. ) in the module. That function will be decorated with a specific decorator (which has open list of perameters, as its' parameters depend on the function decorated).

Note that the function will not be run.

I can of course create the AST from the source, and look there, but TBH it's quite painful. Is there a way where I can load the module and inspect the functions to find whether it's decorated? I also need to get at the time the parameters of the decorator and the signature decorated function.

The use case is that I want to define an entry function in the module, and also provide some more information on the method (which cannot be captured by simple typing) and present in some sort of UI to the users that function and all the information about it (from its signature + the data in the decorator). The decorator's role is also to check those bits at the runtime..

Going back to the simple case say I have a parametric decorator:

def main_function(*ef_args, **ef_kwargs):
    def inner_dec(f):
        def wrapped(*args, **kwargs):
            # use ef_args, ef_kwargs
            response = f(args, kwargs)
            return response
        return wrapped
    return inner_decorator

and then a function in a module file (assume the decorator above is imported):

@main_function(type2="MyFrame", result1="somethign..", result2="somethingelse")
def my_main(param1: string, param2: Frame):
    ....

When I try via inspect, I can see the my_main, but can't see how to tell it's decorated with my decorator, and its signature always comes across as the signature of the decorator. Any ideas? Thanks.


Solution

  • If you change your decorator like so:

    import functools
    def main_function(*ef_args, **ef_kwargs):
        def inner_dec(f):
            # this makes sure that `wrapped` looks like `f`
            # in terms of signature:
            @functools.wraps(f) 
            def wrapped(*args, **kwargs):
                # use ef_args, ef_kwargs
                response = f(args, kwargs)
                return response
            register_my_function(wrapped) # this registers your function
            return wrapped
        return inner_decorator
    

    ... this should register all functions decorated with main_function at import time, and behaves the same at call time.

    (Implementation of register_my_function left up to the reader.)