'Why is mypy trying to instantiate my abstract class in Python?

If I have a Python module like this:

from abc import ABC, abstractmethod

class AbstractClass(ABC):
    @abstractmethod
    def method(self):
        pass

class ConcreteClass1(AbstractClass):
    def method(self):
        print("hello")

class ConcreteClass2(AbstractClass):
    def method(self):
        print("hello")

class ConcreteClass3(AbstractClass):
    def method(self):
        print("hello")

classes = [
    ConcreteClass1,
    ConcreteClass2,
    ConcreteClass3,
]

for c in classes:
    c().method()

And I hit it with mypy test.py, I get this:

test.py:27: error: Cannot instantiate abstract class "AbstractClass" with abstract attribute "method"

The code runs without any issues though, and I can't see any issues in the logic. At no point am I trying to instantiate the AbstractClass directly.

Some weird behavior I've noticed:

If, instead of a loop, I do this:

...
ConcreteClass1().method()
ConcreteClass2().method()
ConcreteClass3().method()

mypy is happy.

Also, if, instead of 3 classes in the loop, I do 2:

classes = [
    ConcreteClass1,
    ConcreteClass2,
    #ConcreteClass3,
]

for c in classes:
    c().method()

mypy is happy with that as well. What's going on? Is this a mypy bug? If so, can I tell mypy to ignore this "problem"?



Solution 1:[1]

The problem appears similar to mypy issues with abstract classes and dictionaries - for some reason, mypy can't typecheck this properly without a type annotation on the list:

classes: list[Type[AbstractClass]] = [
    ConcreteClass1,
    ConcreteClass2,
    ConcreteClass3,
]

(Change list to List if you're on Python 3.8 or below)

You might want to file an issue for this bug on the mypy Github.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1