diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 859bd6afcc6d..5244b1e18ccd 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -526,14 +526,15 @@ def analyze_var(name: str, if typ: if isinstance(typ, PartialType): return mx.chk.handle_partial_var_type(typ, mx.is_lvalue, var, mx.context) - t = get_proper_type(expand_type_by_instance(typ, itype)) if mx.is_lvalue and var.is_property and not var.is_settable_property: # TODO allow setting attributes in subclass (although it is probably an error) mx.msg.read_only_property(name, itype.type, mx.context) if mx.is_lvalue and var.is_classvar: mx.msg.cant_assign_to_classvar(name, mx.context) + t = get_proper_type(expand_type_by_instance(typ, itype)) result = t # type: Type - if var.is_initialized_in_class and isinstance(t, FunctionLike) and not t.is_type_obj(): + typ = get_proper_type(typ) + if var.is_initialized_in_class and isinstance(typ, FunctionLike) and not typ.is_type_obj(): if mx.is_lvalue: if var.is_property: if not var.is_settable_property: @@ -544,7 +545,7 @@ def analyze_var(name: str, if not var.is_staticmethod: # Class-level function objects and classmethods become bound methods: # the former to the instance, the latter to the class. - functype = t + functype = typ # Use meet to narrow original_type to the dispatched type. # For example, assume # * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A) @@ -553,15 +554,17 @@ def analyze_var(name: str, # In `x.f`, when checking `x` against A1 we assume x is compatible with A # and similarly for B1 when checking agains B dispatched_type = meet.meet_types(mx.original_type, itype) - functype = check_self_arg(functype, dispatched_type, var.is_classmethod, + signature = freshen_function_type_vars(functype) + signature = check_self_arg(signature, dispatched_type, var.is_classmethod, mx.context, name, mx.msg) - signature = bind_self(functype, mx.self_type, var.is_classmethod) + signature = bind_self(signature, mx.self_type, var.is_classmethod) + expanded_signature = get_proper_type(expand_type_by_instance(signature, itype)) if var.is_property: # A property cannot have an overloaded type => the cast is fine. - assert isinstance(signature, CallableType) - result = signature.ret_type + assert isinstance(expanded_signature, CallableType) + result = expanded_signature.ret_type else: - result = signature + result = expanded_signature else: if not var.is_ready: mx.not_ready_callback(var.name(), mx.context) diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 09a3616fb3a2..7ec6bfec8446 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2128,3 +2128,54 @@ class B(A): def from_config(cls) -> B: return B() [builtins fixtures/classmethod.pyi] + +[case testAbstractGenericMethodInference] +from abc import ABC, abstractmethod +from typing import Callable, Generic, TypeVar + +A = TypeVar('A') +B = TypeVar('B') +C = TypeVar('C') + +class TwoTypes(Generic[A, B]): + + def __call__(self) -> B: pass + +class MakeTwoAbstract(ABC, Generic[A]): + + def __init__(self) -> None: pass + + @abstractmethod + def __call__(self, b: B) -> TwoTypes[A, B]: pass + +class MakeTwoConcrete(Generic[A]): + + def __call__(self, b: B) -> TwoTypes[A, B]: pass + + +class MakeTwoGenericSubAbstract(Generic[C], MakeTwoAbstract[C]): + + def __call__(self, b: B) -> TwoTypes[C, B]: pass + +class MakeTwoAppliedSubAbstract(MakeTwoAbstract[str]): + + def __call__(self, b: B) -> TwoTypes[str, B]: pass + +class Test(): + + def make_two(self, + mts: MakeTwoAbstract[A], + mte: MakeTwoConcrete[A], + mtgsa: MakeTwoGenericSubAbstract[A], + mtasa: MakeTwoAppliedSubAbstract) -> None: + reveal_type(mts(2)) # N: Revealed type is '__main__.TwoTypes[A`-1, builtins.int*]' + reveal_type(mte(2)) # N: Revealed type is '__main__.TwoTypes[A`-1, builtins.int*]' + reveal_type(mtgsa(2)) # N: Revealed type is '__main__.TwoTypes[A`-1, builtins.int*]' + reveal_type(mtasa(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]' + reveal_type(MakeTwoConcrete[int]()('foo')) # N: Revealed type is '__main__.TwoTypes[builtins.int, builtins.str*]' + reveal_type(MakeTwoConcrete[str]()(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]' + reveal_type(MakeTwoAppliedSubAbstract()('foo')) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.str*]' + reveal_type(MakeTwoAppliedSubAbstract()(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]' + reveal_type(MakeTwoGenericSubAbstract[str]()('foo')) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.str*]' + reveal_type(MakeTwoGenericSubAbstract[str]()(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]' + diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index c4d1bb3d2e53..467579a112d2 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -468,13 +468,13 @@ class B(A[Q]): a: A[int] b: B[str] reveal_type(a.g) # N: Revealed type is 'builtins.int' -reveal_type(a.gt) # N: Revealed type is 'builtins.int*' +reveal_type(a.gt) # N: Revealed type is 'builtins.int' reveal_type(a.f()) # N: Revealed type is 'builtins.int' -reveal_type(a.ft()) # N: Revealed type is '__main__.A*[builtins.int]' +reveal_type(a.ft()) # N: Revealed type is '__main__.A[builtins.int]' reveal_type(b.g) # N: Revealed type is 'builtins.int' -reveal_type(b.gt) # N: Revealed type is 'builtins.str*' +reveal_type(b.gt) # N: Revealed type is 'builtins.str' reveal_type(b.f()) # N: Revealed type is 'builtins.int' -reveal_type(b.ft()) # N: Revealed type is '__main__.B*[builtins.str]' +reveal_type(b.ft()) # N: Revealed type is '__main__.B[builtins.str]' [builtins fixtures/property.pyi] [case testSelfTypeRestrictedMethod] diff --git a/test-data/unit/lib-stub/abc.pyi b/test-data/unit/lib-stub/abc.pyi index 0b1a51c78f5c..da90b588fca3 100644 --- a/test-data/unit/lib-stub/abc.pyi +++ b/test-data/unit/lib-stub/abc.pyi @@ -2,6 +2,7 @@ from typing import Type, Any, TypeVar T = TypeVar('T', bound=Type[Any]) +class ABC(type): pass class ABCMeta(type): def register(cls, tp: T) -> T: pass abstractmethod = object()
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: