1

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.

sauerburger
  • 4,569
  • 4
  • 31
  • 42

1 Answers1

1

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.

sauerburger
  • 4,569
  • 4
  • 31
  • 42