I have a decorator that can be called either without or with arguments (all strings):
@decorator
def fct0(a: int, b: int) -> int:
return a * b
@decorator("foo", "bar") # any number of arguments
def fct1(a: int, b: int) -> int:
return a * b
I am having a hard time providing appropriate type hints so that type checkers will be able to properly validate the usage of the decorator, despite having read the related section of the doc of mypy.
Here is what I have tried so far:
from typing import overload, TypeVar, Any, Callable
F = TypeVar("F", bound=Callable[..., Any])
@overload
def decorator(arg: F) -> F:
...
@overload
def decorator(*args: str) -> Callable[[F], F]:
...
def decorator(*args: Any) -> Any:
# python code adapted from https://stackoverflow.com/q/653368
# @decorator -> shorthand for @decorator()
if len(args) == 1 and callable(args[0]):
return decorator()(args[0])
# @decorator(...) -> real implementation
def wrapper(fct: F) -> F:
# real code using `args` and `fct` here redacted for clarity
return fct
return wrapper
Which results in the following error from mypy
:
error: Overloaded function implementation does not accept all possible arguments of signature 1
I also have an error with pyright
:
error: Overloaded implementation is not consistent with signature of overload 1
Type "(*args: Any) -> Any" cannot be assigned to type "(arg: F@decorator) -> F@decorator"
Keyword parameter "arg" is missing in source
I am using python 3.10.4, mypy 0.960, pyright 1.1.249.