I still think decorator kwarg spec mutations should be possible with a ParamSpec-like interface.
Here’s my latest API suggestion that I believe is both robust and easy to use.
from typing import ParamSpec, Mutate, Callable, TypeVar
P = ParamSpec("P")
T = TypeVar("T")
# `remove` argument is used to define keys that no longer allowed to exist.
# The value of this argument is a list of keys that is no longer allowed to exist
# `add` argument is used to define keys that can now exist
# The value of this argument is a list of tuples. Within the tuple, the first value is the kwarg name, second is the kwarg's type, third value (if provided) indicates that the key is optional since it has a default value.
# If defining a key that already exists within ParamSpec.kwargs, then it is assumed to be overridden with the new type hint.
P_Mutated = Mutate(P, remove=["example_key_1"], add=[("example_key_2", str, "default_value")] )
# Here's what I would expect that to look like in practical use
# Note that KwargSpec should only be allowed within function return type hints, and it can only affect kwargs (since Concantenate already handles args)
def example_wrapper(func: Callable[P], T) -> Callable[P_Mutated, ReturnType]:
...