Search code examples
pythonpython-typingmypy

How to type hint a decorator to dictate some parameters but not all?


I'm looking of a way (the way) to type hint a decorator indicating the parameters that must exist only. The decorator does not change the signature of the function it wraps.

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(a: int, b: str, *args, **kwargs) -> float:
        return func(a, b, *args, **kwargs)

    return wrapper

I've been through a bunch of different attempts. My latest resulted in a MyPy error:

_P = ParamSpec("_P")

def my_decorator(
    func: Callable[[int, str, , _P], flaot]
) -> func: Callable[[int, str, , _P], flaot]:
    ...

Resulted in the error:

error: Invalid location for ParamSpec "_P"  [valid-type]
 note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[_P, int]"

How can I type hint this, such that MyPy will warn if the decorated function has the wrong signature?


Solution

  • typing.Concatenate is used to combine discrete types with a ParamSpec.

    from typing import ParamSpec, Concatenate
    
    
    _P = ParamSpec("_P")
    
    def my_decorator(
        func: Callable[Concatenate[int, str, , _P], float]
    ) -> Callable[Concatenate[int, str, , _P], float]:
        @wraps(func)
        def wrapper(a: int, b: str, *args: _P.args, **kwargs: _P.kwargs) -> float:
            return func(a, b, *args, **kwargs)
    
        return wrapper