python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.13k stars 2.76k forks source link

Type annotation for map with abstract classes as values #3048

Closed sybrenstuvel closed 7 years ago

sybrenstuvel commented 7 years ago

Hello there,

I'm working on a framework with various storage backends. Those backends all implement an abstract base class. The backend classes are stored in a mapping from the backend name to the class implementing that backend.

We want to be able to perform type checking with mypy, and annotate as follows:

import abc
import typing

class A(metaclass=abc.ABCMeta):  # The abstract base class
    def __init__(self, name: str) -> None:
        self.name = name

    @abc.abstractmethod
    def get_name(self):
        pass

class B(A):  # Some non-abstract backend
    def get_name(self):
        return f'B: {self.name}'

class C(A):  # Another non-abstract backend
    def get_name(self):
        return f'C: {self.name}'

backends: typing.Mapping[str, typing.Type[A]] = {
    'backend-b': B,
    'backend-c': C,
}

if __name__ == '__main__':
    backend_cls = backends['backend-c']
    # The following line causes an error with mypy:
    instance = backend_cls('demo-name')
    print(f'Name is: {instance.get_name()}')

Running mypy-0.501 gives this error:

typingtest.py:32: error: Cannot instantiate abstract class 'A' with abstract attribute 'get_name'

My question: How can we annotate the mapping backends such that mypy understands it only contains non-abstract subclasses of A?

gvanrossum commented 7 years ago

Should be fixed by #2853, so closing as a duplicate of #1843.