From 452a64fd1a361204a3123abca57a312e15baa9a3 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Tue, 7 Feb 2017 17:43:08 +0200 Subject: [PATCH] Do not support metaclasses not inheriting from type (#2820) Fix #2818. ABCMeta requires special treatment. Related: #2475 --- mypy/nodes.py | 2 +- mypy/semanal.py | 3 +++ test-data/unit/check-classes.test | 14 +++++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/mypy/nodes.py b/mypy/nodes.py index 4e871209b70a..6cf861a05419 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2024,7 +2024,7 @@ def calculate_metaclass_type(self) -> 'Optional[mypy.types.Instance]': return None def is_metaclass(self) -> bool: - return self.has_base('builtins.type') + return self.has_base('builtins.type') or self.fullname() == 'abc.ABCMeta' def _calculate_is_enum(self) -> bool: """ diff --git a/mypy/semanal.py b/mypy/semanal.py index 0fb02aa5f1cf..dd82975585b8 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -966,6 +966,9 @@ def analyze_metaclass(self, defn: ClassDef) -> None: if not isinstance(sym.node, TypeInfo) or sym.node.tuple_type is not None: self.fail("Invalid metaclass '%s'" % defn.metaclass, defn) return + if not sym.node.is_metaclass(): + self.fail("Metaclasses not inheriting from 'type' are not supported", defn) + return inst = fill_typevars(sym.node) assert isinstance(inst, Instance) defn.info.declared_metaclass = inst diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index b8a7e19c5f0c..3c8bb8e62a03 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2770,9 +2770,9 @@ class B(A, metaclass=Y): pass # E: Inconsistent metaclass structure for 'B' class M: x = 0 # type: int -class A(metaclass=M): pass +class A(metaclass=M): pass # E: Metaclasses not inheriting from 'type' are not supported -reveal_type(A.x) # E: Revealed type is 'builtins.int' +A.x # E: "A" has no attribute "x" [case testMetaclassTypeReveal] from typing import Type @@ -2785,6 +2785,14 @@ 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 testSubclassMetaclass] +class M1(type): + x = 0 +class M2(M1): pass +class C(metaclass=M2): + pass +reveal_type(C.x) # E: Revealed type is 'builtins.int' + [case testMetaclassSubclass] from typing import Type class M(type): @@ -2822,4 +2830,4 @@ from typing import Tuple class M(Tuple[int]): pass class C(metaclass=M): pass # E: Invalid metaclass 'M' -[builtins fixtures/tuple.pyi] \ No newline at end of file +[builtins fixtures/tuple.pyi]