Search code examples
pythonadapter

Dynamic function signature in help()


I am building a library that automatically discovers remote API endpoints and populates an object with methods corresponding to the inferred endpoints. The dynamically created methods have the following structure:

def proxy(*args, **kwds):
    # 1. Validate arguments: Do arguments match remote API?
    # 2. Forward call to remote API
    # 3. Parse and return response
    pass

This works so far. However, I would like to set the method signature shown with help(proxy) dynamically. It would increase the user interface greatly if users can see the inferred signatures directly and also use tab-completion for keyword arguments. Is there a way to decorate, annotate, or otherwise manipulate the function to achieve this?

The difference to Dynamically define functions with varying signature is that I only what to change the signature from the caller perspective. The definition of the method should be with *args and **kwds.


Solution

  • Poking around the source of the inspect module, I realized that the information in __signature__ is honored. Therefore, one can create a new, more informative signature dynamically as follows

    from inspect import Signature, Parameter
    
    def proxy(*args, **kwds):
        pass
    
    user_param = Parameter("user", kind=Parameter.POSITIONAL_OR_KEYWORD)
    comment_param = Parameter("comment", kind=Parameter.POSITIONAL_OR_KEYWORD)
    sig = Signature([user_param, comment_param])
    
    proxy.__signature__ = sig 
    proxy.__doc__ = "Post a new comment as given user"
    proxy.__name__ = "post_comment"
    

    Calling help(proxy) will show

    post_comment(user, comment)
        Post a new comment as given user
    

    and tab-completion suggests user= or comment=.

    Please note, that the new signature is not enforced. Calling proxy(no_such_argument="hello") will not raise a TypeError, so the validation of the signature still needs to take place inside the proxy function.