Here is an expansion of Iced Chai's answer that will also copy over the function signature as well:
from typing import Callable, TypeVar, ParamSpec
P = ParamSpec("P")
T = TypeVar("T")
def wraps(wrapper: Callable[P, T]):
"""An implementation of functools.wraps."""
def decorator(func: Callable) -> Callable[P, T]:
func.__doc__ = wrapper.__doc__
return func
return decorator
Please note that on Python version <= 3.9 ParamSpec is imported from typing_extensions instead from typing. If you need cross version compatibility you can import like this:
import sys
from typing import Callable, TypeVar
if sys.version_info <= (3, 9):
from typing_extensions import ParamSpec
else:
from typing import ParamSpec
Example usage
def func(x: int, y: int) -> str:
"""I have a docstring and arguments"""
@wraps(func)
def anotherfunc(*args, **kwargs):
return func(*args, **kwargs)
Your IDE intellisense/autocomplete should suggest/preview the signature and docstring of func when typing anotherfunc(). I tested this on VSCode and it worked for me. I often use this approach instead of importing functools.wraps because functools.wraps doesn't seem to copying the function signature for VSCode's intellisense.
On VSCode when I type anotherfunc() the intellisense suggestion popup shows:
(x: int, y: int) -> str
-------------------------
I have a docstring
Here is why it works for anyone curious:
Callable[P, T] essentially means "A function that takes in P parameters and returns a type T". ParamSpec reserves not just the argument types, but also their names, order, defaults, and whether they are positional arguments or keyword arguments. Typing two variables with `Callable[P, T] within some common scope is the same as saying "Both of these functions take the same arguments and return the same type"
Here we type hint both the wrapper parameter of wraps and the return type of decorator with Callable[P, T]. This tells editor's static type checker that the resulting function from decoration has the same signature as the input parameter of the wraps function.
More concretely, using the example above, we're saying that the signature of the resulting function from decorating anotherfunc has the same signature as func.
Resources: