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: