8

Can someone explain why inheriting from unparameterized and parameterized Callable:

from typing import Callable
from typing import NoReturn
from typing import TypeVar


T = TypeVar('T', str, int)
C = Callable[[T], NoReturn]


class Foo(Callable):

    def __call__(self, t: T):
        pass


class Bar(C):

    def __call__(self, t: T):
        pass

when passed to mypy raises errors for both Foo and Bar:

tmp.py:13: error: Invalid base class
tmp.py:19: error: Invalid base class
Sean McVeigh
  • 569
  • 3
  • 18

1 Answers1

5

This is in part because classes at runtime can't really inherit from a function or a callable to begin with, and in part because you don't need to explicitly inherit from Callable to indicate that a class is callable.

For example, the following program typechecks as expected using mypy 0.630:

from typing import Callable, Union, NoReturn, List

class Foo:
    def __call__(self, t: Union[str, int]) -> NoReturn:
        pass


class FooChild(Foo): pass


class Bad:
    def __call__(self, t: List[str]) -> NoReturn:
        pass


def expects_callable(x: Callable[[Union[str, int]], NoReturn]) -> None: 
    pass


expects_callable(Foo())         # No error
expects_callable(FooChild())    # No error
expects_callable(Bad())         # Error here: Bad.__call__ has an incompatible signature

Basically, if a class has a __call__ method, it's implicitly assumed that class is also a callable.

Michael0x2a
  • 58,192
  • 30
  • 175
  • 224
  • Is there a way to suppress the check on the class declaration line? – Sean McVeigh Oct 04 '18 at 20:29
  • @SeanMcVeigh -- sorry, which check are you referring to? – Michael0x2a Oct 04 '18 at 20:31
  • Why does mypy even raise an error given that subclassing Callable is really to take advantage of `ABCMeta` – Sean McVeigh Oct 04 '18 at 20:31
  • sorry, i mean whatever raises `error: Invalid base class` – Sean McVeigh Oct 04 '18 at 20:32
  • @SeanMcVeigh -- I guess you could add on a `# type: ignore` comment to that line. Alternatively, you could not inherit from Callable and just mark Foo's `__call__` as an abstract method (e.g. using abc's `@abstractmethod` decorator). That would let you mandate all subclasses implement `__call__` with a specific signature. – Michael0x2a Oct 04 '18 at 20:40