-
-
Notifications
You must be signed in to change notification settings - Fork 32.7k
Description
Feature or enhancement
Add an decorator that copies the parameter specs from one function onto another:
from collections.abc import Callable
from typing import ParamSpec, TypeVar, cast, Any
P = ParamSpec("P")
T = TypeVar("T")
def copy_kwargs(
kwargs_call: Callable[P, Any]
) -> Callable[[Callable[..., T]], Callable[P, T]]:
"""Decorator does nothing but returning the casted original function"""
@wraps(kwargs_call)
def return_func(func: Callable[..., T]) -> Callable[P, T]:
return cast(Callable[P, T], func)
return return_func
Alternative names of copy_kwargs
could be apply_parameters
Pitch
A quite common pattern in Python is to create a new function enhanced
that enhanced a given function original
or method and passes arguments using *args, **kwargs
.
This way the signature of original
can be adopted without changing the signature of enhanced
.
That is especially useful if you enhance a 3rd party function.
A downside of this pattern is, that static type checkers (and IDE) are unable to detect if the correct parameters were used or give type/autocomplete hints.
Adding this pattern allows static type checkers and IDE to give correct parameter hints and check them.
Previous discussion
Discussed with @ambv at a Sprint within Europython 2023
Specification
This function is very simple, so adding it every project that requires it would be very simple.
A reason to add it to the standard library is that type checkers can/should check, that the applied parameter spec matches the one of the function.
In example:
# Our test function for kwargs
def source_func(foo: str, bar: int, default: bool = True) -> str:
if not default:
return "Not Default!"
return f"{foo}_{bar}"
@copy_kwargs(source_func)
def kwargs_test(**kwargs) -> float:
print(source_func(**kwargs))
return 1.2
kwargs_test("a", 2) # raises a TypeError but does not produce any TypingError atm
But if source_func
would be defined as
def source_func(*, foo: str, bar: int, default: bool = True) -> str:
...
The type checker would complain.
So I would suggest that type checkers check if the wrapped functions signature is compatible to the sourced function signature.
Which is separate discussion from including it into the stdlib.
The documentation should include a hint, that *args
and **kwargs
should be always added to the applied function.
Related Discourse Threads: #
- Add a
same_definition_as_in
- Taking the argument signature from a different function
- Proposal: Add
bound
toParamSpec
- Precise typing of kwargs but without a TypedDict
- Extract kwargs types from a function signature
- Dynamically building ParamSpecs from callables
Related Issues:
- How should we annotate functions that forward to their superclass? How should we annotate functions that forward to their superclass? typing#1471 by @NeilGirdhar