I want to do something like
from typing import Callable
def a(foo: Callable[[int], None]):
foo(b=5)
This code works, but gives a warning Unexpected argument.
Defining as
def a(foo: Callable[[int], None]):
foo(5)
works with no warnings as expected.
How can I pass in an expected argument as a kwarg into a function with the type checker not being angry at me?
The Callable docs say
There is no syntax to indicate optional or keyword arguments; such function types are rarely used as callback types.
However they also say
Callable[..., ReturnType](literal ellipsis) can be used to type hint a callable taking any number of arguments and returningReturnType
Applying here, that would be
def a(foo: Callable[..., None]):
You will lose the int annotation, but it's either that, living with the warning or explicitly surpassing it.
You can use a "callable protocol" here, so:
import typing
class MyCallableType(typing.Protocol):
def __call__(self, bar:int) -> None:
...
def a(foo: MyCallableType):
foo(32)
foo(bar=32)
Now, testing the above with mypy:
jarrivillaga$ mypy --version
mypy 0.910
jarrivillaga$ mypy test.py
Success: no issues found in 1 source file
Note, this allows mypy to catch all kinds of errors, e.g. a function with the wrong argument name, or if we want b to be a function that specifies a keyword-only bar argument:
import typing
class MyCallableType(typing.Protocol):
def __call__(self, b:int) -> None:
...
def a(foo: MyCallableType):
foo(32)
foo(b=32)
def bar(b: int) -> None:
pass
def baz(*, b: int) -> None:
pass
def bing(x: int) -> None:
pass
a(bar)
a(baz)
a(bing)
And mypy will complain with the following:
jarrivillaga$ mypy test.py
test.py:21: error: Argument 1 to "a" has incompatible type "Callable[[NamedArg(int, 'b')], None]"; expected "MyCallableType"
test.py:22: error: Argument 1 to "a" has incompatible type "Callable[[int], None]"; expected "MyCallableType"
Found 2 errors in 1 file (checked 1 source file)