Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enumerating concrete subclasses of an abstract base class causes false positive "Cannot instantiate abstract class X with abstract attribute Y" #16274

Open
poppadum opened this issue Oct 17, 2023 · 1 comment
Labels
bug mypy got something wrong

Comments

@poppadum
Copy link

I have a number of concrete classes which all derive from the same abstract base class. Creating a list of instances using __subclasses__() in a list comprehension, mypy complains that I'm trying to instantiate the abstract class.

Simple example:
https://mypy-play.net/?mypy=latest&python=3.11&gist=f03c51fac1173ae0bb751b00e614de68

from abc import ABC, abstractmethod
from typing import List

class Animal(ABC):
    @abstractmethod
    def talk(self) -> str:
        pass

class Dog(Animal):
    def talk(self) -> str:
        return "woof"

class Cat(Animal):
    def talk(self) -> str:
        return "miaow"

# enumerate using __subclasses__()
animals: List[Animal] = [
    subclass() for subclass in Animal.__subclasses__()
]
print(animals)

# hard-coding subclass instances is fine
animals2: List[Animal] = [
     Dog(), Cat()
]

Expected Behavior
As I'm not actually instantiating the abstract class, the mypy error looks like a false positive.

Your Environment
python 3.10.12
mypy v1.6.0

Possibly related issues
#15554
#14106
#3115

@darthwalsh
Copy link

@poppadum I think the mypy hint is right here, because __subclasses__() returns list[type[Animal]] and any of these could by abstract types.

Here's a different example:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def talk(self) -> str:
        pass

class Dog(Animal):
    def talk(self) -> str:
        return "woof"

class Reptile(Animal):
    def talk(self) -> str:
        return "hiss"
    
    @abstractmethod
    def egg_type(self) -> str:
        pass

animals: list[Animal] = [
    subclass() for subclass in Animal.__subclasses__()
]
print(animals)

And creating Reptile() will fail:

$ python3 a.py
Traceback (most recent call last):
  File "/Users/walshca/a.py", line 20, in <module>
    animals: list[Animal] = [
                            ^
  File "/Users/walshca/a.py", line 21, in <listcomp>
    subclass() for subclass in Animal.__subclasses__()
    ^^^^^^^^^^
TypeError: Can't instantiate abstract class Reptile with abstract method egg_type
NativeCommandExitException: Program "python3" ended with non-zero exit code: 1.

$ mypy a.py
a.py:21: error: Cannot instantiate abstract class "Animal" with abstract attribute "talk"  [abstract]
Found 1 error in 1 file (checked 1 source file)
NativeCommandExitException: Program "mypy" ended with non-zero exit code: 1.

Instead, "enumerating concrete subclasses" would need to return something like typing.Concrete[Animal] (which doesn't exist), filtering __subclasses__() somehow (and probably recurse through subclasses of subclasses)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

2 participants