I’m trying to figure out how to write a decorator that passes strict type checks as well as preserve type information about the original arguments of a function.
What makes this case tricky, is that the decorator must drop the first argument of the function it is wrapping.
I’ve been able to get past most of the typing/mypy errors using Concatenate
, ParamSpec
etc…, but now I’m stuck.
Here is what I have so far:
from functools import wraps
from typing import Any, Callable, Concatenate, ParamSpec, TypeVar
P = ParamSpec('P')
R = TypeVar('R')
def drop_first_arg(f: Callable[Concatenate[Any, P], R]) -> Callable[P, R]:
@wraps(f)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
# Error on "return" line:
# MyPy(4.10.0): error: Argument 1 has incompatible type "*tuple[object, ...]";
# expected "P.args" [arg-type]
# Pylance: Arguments for ParamSpec "P@drop_first_arg" are missing
# (reportCallIssue)
return f(*args[1:], **kwargs) # `[1:]` effectively drops the first argument
return wrapper
When you pass the above through mypy --strict
, then you’ll see the following error message:
error: Argument 1 has incompatible type "*tuple[object, ...]"; expected "P.args" [arg-type]
So essentially this all boils down to the question: How can I write a drop_first_argument
decorator that will also pass all of mypy --strict
‘s checks?