From b3bfeb28e451974a838c5dd21a8da7970db81aa4 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 31 Oct 2021 22:00:49 +0300 Subject: [PATCH] Now `__new__` in a metaclass must return a subtype of `type`, refs #11398 --- mypy/checker.py | 12 +++++++++++- test-data/unit/check-classes.test | 31 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 9c389a3d08d8..78a374379f3e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1104,7 +1104,17 @@ def check___new___signature(self, fdef: FuncDef, typ: CallableType) -> None: bound_type = bind_self(typ, self_type, is_classmethod=True) # Check that __new__ (after binding cls) returns an instance # type (or any). - if not isinstance(get_proper_type(bound_type.ret_type), + if isinstance(fdef.info, TypeInfo) and fdef.info.is_metaclass(): + # This is a metaclass, so it must return a new unrelated type. + self.check_subtype( + bound_type.ret_type, + self.type_type(), + fdef, + message_registry.INVALID_NEW_TYPE, + 'returns', + 'but must return a subtype of' + ) + elif not isinstance(get_proper_type(bound_type.ret_type), (AnyType, Instance, TupleType)): self.fail( message_registry.NON_INSTANCE_NEW_TYPE.format( diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 62641be92727..070fdaff27c0 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6419,6 +6419,37 @@ class B(A): reveal_type(B()) # N: Revealed type is "__main__.B" +[case testNewReturnType10] +# https://github.com/python/mypy/issues/11398 +from typing import Type + +class MyMetaClass(type): + def __new__(cls, name, bases, attrs) -> Type['MyClass']: + pass + +class MyClass(metaclass=MyMetaClass): + pass + +[case testNewReturnType11] +# https://github.com/python/mypy/issues/11398 +class MyMetaClass(type): + def __new__(cls, name, bases, attrs) -> type: + pass + +class MyClass(metaclass=MyMetaClass): + pass + +[case testNewReturnType12] +# https://github.com/python/mypy/issues/11398 +from typing import Type + +class MyMetaClass(type): + def __new__(cls, name, bases, attrs) -> int: # E: Incompatible return type for "__new__" (returns "int", but must return a subtype of "type") + pass + +class MyClass(metaclass=MyMetaClass): + pass + [case testGenericOverride] from typing import Generic, TypeVar, Any