Skip to content

Metaclass is not properly inherited when using multiple inheritance with int #2824

Closed
python/typeshed
#1598

Description

@gvanrossum

Consider:

class M(type):
    def foo(self): pass
class C(metaclass=M):
    pass
print(C.__class__)
C.foo()
class D(int, C):
    pass
print(D.__class__)
D.foo()

Running this with Python 3 shows that D's metaclass is M, but when checking this with mypy, we get the error

u.py:10: error: "D" has no attribute "foo"

on the last line, showing that mypy doesn't believe so.

This seems unique to int (or perhaps builtin types?) -- when create a separate class A (without a metaclass) and use that instead of int, there is no error.

FWIW this prevents me from fixing #2305 (iterating on enums) -- I can get it to work for Enum but not for IntEnum, because it multiply inherits from int and from Enum.

Activity

  1. elazarg commented on Feb 7, 2017

    @elazarg
    Contributor

    It does not reproduce in unit tests.

  2. elazarg commented on Feb 7, 2017

    @elazarg
    Contributor

    In general, the class/metaclass hierarchy in typeshed does not mirror the hierarchy in Python. I'm not sure, but it could be the reason. Perhaps the special casing of ABCMeta in mypy is also a problem.

  3. elazarg commented on Feb 7, 2017

    @elazarg
    Contributor

    Removing SupportsInt and SupportsFloat from baseclasses makes the error go away. SupportsAbs[int] has nothing to do with the error. Since the formers have ABCMeta as a metaclass, and the latter does not, It points to ABCMeta as the problem.

  4. elazarg commented on Feb 7, 2017

    @elazarg
    Contributor

    Well I guess I'm completely wrong. Perhaps it's a simple case of choosing the most derived common metaclass. In typeshed type(int) == ABCMeta. In Python type(int) == type.

  5. elazarg commented on Feb 7, 2017

    @elazarg
    Contributor

    A possible solution: make EnumMeta inherit ABCMeta, so it will be the most derived common metaclass.

  6. gvanrossum commented on Feb 8, 2017

    @gvanrossum
    MemberAuthor

    That mostly works, but I needed to add a # type: ignore. Thoughts? https://github.com/python/typeshed/tree/enum-iter

  7. elazarg commented on Feb 9, 2017

    @elazarg
    Contributor

    Is there some problem with selftype? I have encountered something, but I wasn't sure.

  8. gvanrossum commented on Feb 9, 2017

    @gvanrossum
    MemberAuthor

    Without that I get

    typeshed/stdlib/3.4/enum.pyi:11: error: The erased type of self 'Type[enum.Enum]' is not a supertype of its class 'enum.EnumMeta'
    

    FWIW the test program is

    import enum
    class E(enum.Enum):
        a = 1
        b = 2
    for i in E:
        reveal_type(i)
    class I(enum.IntEnum):
        a = 1
        b = 2
    for k in I:
        reveal_type(k)

    and its output (less the error) is

    t.py:6: error: Revealed type is 't.E*'
    t.py:11: error: Revealed type is 't.I*'
    

    which is exactly what it should be.

  9. elazarg commented on Feb 9, 2017

    @elazarg
    Contributor

    There are additional checks to add to the subtype visitor that makes this problem go away, but something bothers me. The variable seems to be overly constrained by a class not defined yet. We should add another check for the metaclass declaration, to verify that we are constrained correctly by the type of cls.

  10. elazarg commented on Feb 9, 2017

    @elazarg
    Contributor

    As I have mentioned in a different discussion here, it might make sense to use the form cls: Meta[T] which means precisely the type whose metaclass is a subclass of Meta (directly inheriting the generic [] operator from Type). Then we don't need to constrain further T in any way.

  11. elazarg commented on Sep 8, 2017

    @elazarg
    Contributor
  12. JelleZijlstra commented on Sep 11, 2017

    @JelleZijlstra
    Member

    Recap:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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