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

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

Closed
eddieantonio opened this issue Mar 21, 2017 · 5 comments

Comments

@eddieantonio
Copy link

eddieantonio commented Mar 21, 2017

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.

@gvanrossum
Copy link
Member

I've seen this too (there may even be a duplicate issue). I don't think 2922 is related. However I do think the join() implementation inside mypy could be taught to do this better. It's not very common, and the workaround (add an explicit type) is easy enough.

@miedzinski
Copy link
Contributor

I'd like to fix this.

@gvanrossum
Copy link
Member

Go for it!

@miedzinski
Copy link
Contributor

Sorry for not updating this. I did git bisect and found that issue was fixed in 00359ad (#2833).

@gvanrossum
Copy link
Member

Thanks, closing.

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

No branches or pull requests

3 participants