pythonmypypython-typinghexagonal-architecture

Calling instance of adapter class plugged into port is causing mypy issues


I'm currently plugging an Adapter into a port in a handler which can help you get an instance of that port. Here is a simple setup to reproduce:

from typing import Protocol


class SomePort(Protocol):
    def caller(self):
        raise NotImplementedError


class SomeAdapter:
    def caller(self):
        print("Called")


class SomeController:
    @staticmethod
    def get_instance(port: SomePort) -> SomePort:
        return port()


instance = SomeController.get_instance(port=SomeAdapter)
instance.caller()

This is working just fine in code but in mypy I'm getting the following issues:

$ mypy --version
mypy 0.931

$ mypy test/test.py 
test/test.py:17: error: "SomePort" not callable
test/test.py:20: error: Argument "port" to "get_instance" of "SomeController" has incompatible type "Type[SomeAdapter]"; expected "SomePort"

anything I've misunderstood here? Running the script prints Called just fine, but mypy is unhappy about the way I've set this up. Any help is appreciated. :)


Solution

  • If you want the port parameter in SomeController.get_instance to accept a class that is a subtype of SomePort, you need to annotate it with type[SomePort] instead of SomePort.

    The latter (as you have it in your example) would express that port should be an instance of the type SomePort. Since SomePort is not callable (no __call__ method defined), you get that first Mypy error. And because SomeAdapter is a class that follows the SomePort protocol rather than an instance of such a class, you get that second error.

    If you are on Python <3.9 use typing.Type instead of the built-in type, i.e. typing.Type[SomePort].