And suppose this decorator doesn't change the wrapped function signature (e.g. it just build some context or check some inputs). The code might have something like this (before returning the wrapper):
wrapper.__signature__ = inspect.signature(func)
It would help a lot if the list of valid arguments would be kept in the help message of the wrapped function.
If the wrapper function changes the signature, one could just create another inspect.Signature object to properly document its behavior. Setting the __signature__ would suffice for that. Actually, the same applies to other functions, not just wrappers. This might not be possible beforehand as the parameter names/kinds/defaults might be dynamic (e.g. the distinct signatures of the wrapped functions).
# Second idea:
The partial objects might include an automatic propagation of the __signature__, if there's one already set. That can be lazy (the partial object __signature__ is created only when requested).
# Third idea:
Setting the function signature itself from an inspect.Signature object.
That might be a new functools.wraps keyword argument, like:
@functools.wraps(wrapped_func, signature=wrapped_func.__signature__)
def wrapper_func(*args, **kwargs):
... # some code
Or perhaps a new syntax, like:
def wrapper_func(*args, **kwargs) with wrapped_func.__signature__:
... # some code
Where the signature object is an inspect.Signature instance, or perhaps a callable to have its signature copied from.
Externally, this would behave like a function with the given (more restrict) signature (e.g. raising TypeError if it doesn't match the given wrapped_func.__signature__ in the example). Internally, the function would just use the explicit args and kwargs (or whatever the explicit signature happen to be).
As an example, suppose the wrapped_func has the (a, b, c=3, *, d=4) signature, the wrapper_func of the proposed syntax would behave like this for this example:
def wrapper_func(a, b, c=3, *, d=4):
def internal_wrapper_func(*args, **kwargs):
... # some code
return internal_wrapper_func(a, b, c=3, *, d=4)