From c95ecc1952b4f804662e37b36c0779b32226f88b Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 9 Oct 2019 13:18:42 -0700 Subject: [PATCH] Only return a declared type from __new__ if it is a subtype of the class (#7656) Fixes #7597. --- mypy/typeops.py | 15 ++++++++++++--- test-data/unit/check-classes.test | 12 +++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 6e8f7846c714..36a7597ec54d 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -77,11 +77,20 @@ def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance, variables.extend(info.defn.type_vars) variables.extend(init_type.variables) + from mypy.subtypes import is_subtype + init_ret_type = get_proper_type(init_type.ret_type) - if is_new and isinstance(init_ret_type, (Instance, TupleType)): - ret_type = init_type.ret_type # type: Type + default_ret_type = fill_typevars(info) + if ( + is_new + and isinstance(init_ret_type, (Instance, TupleType)) + # Only use the return type from __new__ if it is actually returning + # a subtype of what we would return otherwise. + and is_subtype(init_ret_type, default_ret_type, ignore_type_params=True) + ): + ret_type = init_ret_type # type: Type else: - ret_type = fill_typevars(info) + ret_type = default_ret_type callable_type = init_type.copy_modified( ret_type=ret_type, fallback=type_type, name=None, variables=variables, diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index e0c8853fb0ce..59b0e528a1f2 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -6031,7 +6031,7 @@ class A: def __new__(cls) -> int: # E: Incompatible return type for "__new__" (returns "int", but must return a subtype of "A") pass -reveal_type(A()) # N: Revealed type is 'builtins.int' +reveal_type(A()) # N: Revealed type is '__main__.A' [case testNewReturnType4] from typing import TypeVar, Type @@ -6099,6 +6099,16 @@ class X: def __new__(cls, x: TX) -> TX: # E: "__new__" must return a class instance (got "TX") pass +[case testNewReturnType9] +class A: + def __new__(cls) -> A: + pass + +class B(A): + pass + +reveal_type(B()) # N: Revealed type is '__main__.B' + [case testGenericOverride] from typing import Generic, TypeVar, Any