Reputation: 595
I have code that looks like the following:
from typing import Callable
def decorate(func: Callable[[str], None]) -> Callable[[str], None]:
return func
@decorate
def do_something(some_str: str = 'Hello world') -> None:
print(some_str)
if __name__ == '__main__':
do_something()
When running mypy, it reports the following error for the last line:
error: Too few arguments for "do_something" [call-arg]
How can I fix this error (without changing the return type of decorate
to Callable[..., None]
)?
Upvotes: 2
Views: 2877
Reputation: 52169
Use a TypeVar
to indicate that the input and return type are equivalent and defined by the Callable
received. Since Callable
types include the entire signature, this lets the type checker infer positional/optional/keyword arguments as well.
from typing import Callable, TypeVar
# A type variable that can represent any Callable type
C = TypeVar("C", bound=Callable)
# The input Callable type defines the output Callable type as well
def decorate(func: C) -> C:
return func
If decorate
can only work with callables that may take a string, adjust the bound
accordingly.
# A type variable that can represent any Callable type that takes a string
C = TypeVar("C", bound=Callable[[str], None])
Notably, a callable taking a str
or using a default still satisfies Callable[[str], None]
since it can be called with a str
argument.
Upvotes: 1
Reputation: 7887
Protocol
and __call__
give a lot of (somewhat verbose) power when expressing function types
class Foo(Protocol):
def __call__(self, some_str: str = 'Hello world') -> None:
...
def decorate(func: Foo) -> Foo:
return func
@decorate
def do_something(some_str: str = 'Hello world') -> None:
print(some_str)
if __name__ == '__main__':
do_something()
Not sure how to satisfyingly avoid the "Hello World!" duplication.
Upvotes: 1