Skip to content

Surprising inferred common super class of iterable when using classes inheriting from abc.ABC #3042

Closed
@eddieantonio

Description

@eddieantonio

When I create an iterator of classes, all of which inherit from the same super class, itself inheriting from abc.ABC, I expect the inferred type to be an Iterator[Type[common super class]]. However, instead I get Iterator[abc.ABCMeta*], which seems to be a bit too far up the inheritance chain for me. Note that when I infer the types of instances of these same classes, I do get Iterator[Edit*], as expected.

Below is a simplified example of when I got this behavior:

# example.py
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING


class Edit(ABC):
    @classmethod
    @abstractmethod
    def do_something(cls) -> None: ...


class Insertion(Edit):
    @classmethod
    def do_something(cls):
        print("foo")


class Deletion(Edit):
    @classmethod
    def do_something(cls):
        print("foo")


class Substitution(Edit):
    @classmethod
    def do_something(cls):
        print("baz")


if not TYPE_CHECKING:
    # define reveal_type as a noop outside of mypy
    reveal_type = lambda *x: ...

# mypy infers common subclass for instances.
instances = Insertion(), Deletion(), Substitution()
# Revealed type is 'typing.Iterator[example.Edit*]
reveal_type(iter(instances))

# mypy does NOT infer common subclass for classes themselves.
classes = [Insertion, Deletion, Substitution]
# This assert passes; however...
assert all(issubclass(cls, Edit) for cls in classes)
# Revealed type is 'typing.Iterator[abc.ABCMeta*]
reveal_type(iter(classes))
# I expect: Revealed type is 'typing.Iterator[example.Edit*]

for cls in Insertion, Deletion, Substitution:
    # Revealed type is 'abc.ABCMeta'
    reveal_type(cls)
    # I expect: Revealed Type is 'Type[example.Edit]'

This is a simplified diagram of the inheritance hierarchy:

                            ABC (metaclass=ABCMeta)
                             ^
                             |
                            Edit
                             ^
         ,-------------------'-----------------.
     Insertion            Deletion        Substitution

Explcitly typing works, however:

from typing import Sequence, Type
klasses: Sequence[Type[Edit]] = [Insertion, Deletion, Substitution]

for kls in klasses:
    # Revealed type is 'Type[example.Edit]'
    reveal_type(kls)

Possibly related to: #2922.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions