How to get rid of 'incompatible type "Callable[[NamedArg(int, 'val')], ..'?

150 Views Asked by At

I'm trying to forward and/or store Callables while providing as much type hinting as possible. Currently I'm struggling with a mypy error that seems simple, but I somehow cannot solve it. Imagine this little snippet containing a collection of Callables:

from collections.abc import Callable, MutableSequence

functions: MutableSequence[Callable[[int], None]] = []

def foo(val: int) -> None:
    print(val)

functions.append(foo)

While somewhat senseless, it works and mypy --strict gives me zero issues.

Now I want to make foo accept only named parameters:

def foo(*, val: int) -> None:

and mypy gives me

./foo.py:11: error: Argument 1 to "append" of "MutableSequence" has incompatible type "Callable[[NamedArg(int, 'val')], None]"; expected "Callable[[int], None]"  [arg-type]

.. which sounds plausible, but is there a way to get around this? NamedArg can't be imported via typing or typing_extensions, but only via mypy_extensions, which feels strange to have it as a dependency. But even when I accept it, it gives me a class, rather than some Generic.

How can I solve this without losing type hinting for the named arguments?

Btw, in a real-life-project I'm forwarding kw-args, so allowing the list in the snippet above to only accept a kw-arg with one (generic) argument name like MutableSequence[Callable[[NamedArg(int, 'val')], None]] would be fine.

1

There are 1 best solutions below

0
Robin Gugel On

What you need if the Callable[...] syntax doesn't suffice to define your signature is a Callback Protocol.

Applied to your example it'd look like this:

from collections.abc import MutableSequence
from typing import Protocol

class FooCallable(Protocol):

    def __call__(self, *, val: int) -> None:
        ...

functions: MutableSequence[FooCallable] = []

def foo(*, val: int) -> None:
    print(val)

functions.append(foo)