Skip to content

Commit b731899

Browse files
elazargJukkaL
authored andcommitted
Calculate implicit metaclasses (#2819)
Fix an issue in #2475 - metaclasses are not calculated for subclasses (this is needed for Enum).
1 parent a2d654b commit b731899

File tree

4 files changed

+20
-5
lines changed

4 files changed

+20
-5
lines changed

mypy/checkmember.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def analyze_member_access(name: str,
134134
if not is_operator:
135135
# When Python sees an operator (eg `3 == 4`), it automatically translates that
136136
# into something like `int.__eq__(3, 4)` instead of `(3).__eq__(4)` as an
137-
# optimation.
137+
# optimization.
138138
#
139139
# While it normally it doesn't matter which of the two versions are used, it
140140
# does cause inconsistencies when working with classes. For example, translating

mypy/nodes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,6 +2017,8 @@ def calculate_metaclass_type(self) -> 'Optional[mypy.types.Instance]':
20172017
return mypy.types.Instance(self, [])
20182018
candidates = [s.declared_metaclass for s in self.mro if s.declared_metaclass is not None]
20192019
for c in candidates:
2020+
if c.type.mro is None:
2021+
continue
20202022
if all(other.type in c.type.mro for other in candidates):
20212023
return c
20222024
return None

mypy/semanal.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -969,10 +969,11 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
969969
inst = fill_typevars(sym.node)
970970
assert isinstance(inst, Instance)
971971
defn.info.declared_metaclass = inst
972-
defn.info.metaclass_type = defn.info.calculate_metaclass_type()
973-
if defn.info.metaclass_type is None:
974-
# Inconsistency may happen due to multiple baseclasses even in classes that
975-
# do not declare explicit metaclass, but it's harder to catch at this stage
972+
defn.info.metaclass_type = defn.info.calculate_metaclass_type()
973+
if defn.info.metaclass_type is None:
974+
# Inconsistency may happen due to multiple baseclasses even in classes that
975+
# do not declare explicit metaclass, but it's harder to catch at this stage
976+
if defn.metaclass:
976977
self.fail("Inconsistent metaclass structure for '%s'" % defn.name, defn)
977978

978979
def object_type(self) -> Instance:

test-data/unit/check-classes.test

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2785,6 +2785,18 @@ def f(TA: Type[A]):
27852785
reveal_type(TA) # E: Revealed type is 'Type[__main__.A]'
27862786
reveal_type(TA.x) # E: Revealed type is 'builtins.int'
27872787

2788+
[case testMetaclassSubclass]
2789+
from typing import Type
2790+
class M(type):
2791+
x = 0 # type: int
2792+
2793+
class A(metaclass=M): pass
2794+
class B(A): pass
2795+
2796+
def f(TB: Type[B]):
2797+
reveal_type(TB) # E: Revealed type is 'Type[__main__.B]'
2798+
reveal_type(TB.x) # E: Revealed type is 'builtins.int'
2799+
27882800
[case testMetaclassIterable]
27892801
from typing import Iterable, Iterator
27902802

0 commit comments

Comments
 (0)