Skip to content

Commit

Permalink
Calculate implicit metaclasses (#2819)
Browse files Browse the repository at this point in the history
Fix an issue in #2475 - metaclasses are not calculated for subclasses (this 
is needed for Enum).
  • Loading branch information
elazarg authored and JukkaL committed Feb 7, 2017
1 parent a2d654b commit b731899
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 5 deletions.
2 changes: 1 addition & 1 deletion mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def analyze_member_access(name: str,
if not is_operator:
# When Python sees an operator (eg `3 == 4`), it automatically translates that
# into something like `int.__eq__(3, 4)` instead of `(3).__eq__(4)` as an
# optimation.
# optimization.
#
# While it normally it doesn't matter which of the two versions are used, it
# does cause inconsistencies when working with classes. For example, translating
Expand Down
2 changes: 2 additions & 0 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2017,6 +2017,8 @@ def calculate_metaclass_type(self) -> 'Optional[mypy.types.Instance]':
return mypy.types.Instance(self, [])
candidates = [s.declared_metaclass for s in self.mro if s.declared_metaclass is not None]
for c in candidates:
if c.type.mro is None:
continue
if all(other.type in c.type.mro for other in candidates):
return c
return None
Expand Down
9 changes: 5 additions & 4 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,10 +969,11 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
inst = fill_typevars(sym.node)
assert isinstance(inst, Instance)
defn.info.declared_metaclass = inst
defn.info.metaclass_type = defn.info.calculate_metaclass_type()
if defn.info.metaclass_type is None:
# Inconsistency may happen due to multiple baseclasses even in classes that
# do not declare explicit metaclass, but it's harder to catch at this stage
defn.info.metaclass_type = defn.info.calculate_metaclass_type()
if defn.info.metaclass_type is None:
# Inconsistency may happen due to multiple baseclasses even in classes that
# do not declare explicit metaclass, but it's harder to catch at this stage
if defn.metaclass:
self.fail("Inconsistent metaclass structure for '%s'" % defn.name, defn)

def object_type(self) -> Instance:
Expand Down
12 changes: 12 additions & 0 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -2785,6 +2785,18 @@ def f(TA: Type[A]):
reveal_type(TA) # E: Revealed type is 'Type[__main__.A]'
reveal_type(TA.x) # E: Revealed type is 'builtins.int'

[case testMetaclassSubclass]
from typing import Type
class M(type):
x = 0 # type: int

class A(metaclass=M): pass
class B(A): pass

def f(TB: Type[B]):
reveal_type(TB) # E: Revealed type is 'Type[__main__.B]'
reveal_type(TB.x) # E: Revealed type is 'builtins.int'

[case testMetaclassIterable]
from typing import Iterable, Iterator

Expand Down

0 comments on commit b731899

Please sign in to comment.