Skip to content

Commit 9d5d0e9

Browse files
beezeemsullivan
authored andcommitted
Fix errors in decorated generic methods (#7933)
Type variables need to be freshened in this case as well. Fixes #7863.
1 parent bbb192d commit 9d5d0e9

File tree

4 files changed

+67
-12
lines changed

4 files changed

+67
-12
lines changed

mypy/checkmember.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -528,14 +528,15 @@ def analyze_var(name: str,
528528
if typ:
529529
if isinstance(typ, PartialType):
530530
return mx.chk.handle_partial_var_type(typ, mx.is_lvalue, var, mx.context)
531-
t = get_proper_type(expand_type_by_instance(typ, itype))
532531
if mx.is_lvalue and var.is_property and not var.is_settable_property:
533532
# TODO allow setting attributes in subclass (although it is probably an error)
534533
mx.msg.read_only_property(name, itype.type, mx.context)
535534
if mx.is_lvalue and var.is_classvar:
536535
mx.msg.cant_assign_to_classvar(name, mx.context)
536+
t = get_proper_type(expand_type_by_instance(typ, itype))
537537
result = t # type: Type
538-
if var.is_initialized_in_class and isinstance(t, FunctionLike) and not t.is_type_obj():
538+
typ = get_proper_type(typ)
539+
if var.is_initialized_in_class and isinstance(typ, FunctionLike) and not typ.is_type_obj():
539540
if mx.is_lvalue:
540541
if var.is_property:
541542
if not var.is_settable_property:
@@ -546,7 +547,7 @@ def analyze_var(name: str,
546547
if not var.is_staticmethod:
547548
# Class-level function objects and classmethods become bound methods:
548549
# the former to the instance, the latter to the class.
549-
functype = t
550+
functype = typ
550551
# Use meet to narrow original_type to the dispatched type.
551552
# For example, assume
552553
# * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A)
@@ -555,15 +556,17 @@ def analyze_var(name: str,
555556
# In `x.f`, when checking `x` against A1 we assume x is compatible with A
556557
# and similarly for B1 when checking agains B
557558
dispatched_type = meet.meet_types(mx.original_type, itype)
558-
functype = check_self_arg(functype, dispatched_type, var.is_classmethod,
559+
signature = freshen_function_type_vars(functype)
560+
signature = check_self_arg(signature, dispatched_type, var.is_classmethod,
559561
mx.context, name, mx.msg)
560-
signature = bind_self(functype, mx.self_type, var.is_classmethod)
562+
signature = bind_self(signature, mx.self_type, var.is_classmethod)
563+
expanded_signature = get_proper_type(expand_type_by_instance(signature, itype))
561564
if var.is_property:
562565
# A property cannot have an overloaded type => the cast is fine.
563-
assert isinstance(signature, CallableType)
564-
result = signature.ret_type
566+
assert isinstance(expanded_signature, CallableType)
567+
result = expanded_signature.ret_type
565568
else:
566-
result = signature
569+
result = expanded_signature
567570
else:
568571
if not var.is_ready:
569572
mx.not_ready_callback(var.name, mx.context)

test-data/unit/check-generics.test

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2287,3 +2287,54 @@ class B(A):
22872287
def from_config(cls) -> B:
22882288
return B()
22892289
[builtins fixtures/classmethod.pyi]
2290+
2291+
[case testAbstractGenericMethodInference]
2292+
from abc import ABC, abstractmethod
2293+
from typing import Callable, Generic, TypeVar
2294+
2295+
A = TypeVar('A')
2296+
B = TypeVar('B')
2297+
C = TypeVar('C')
2298+
2299+
class TwoTypes(Generic[A, B]):
2300+
2301+
def __call__(self) -> B: pass
2302+
2303+
class MakeTwoAbstract(ABC, Generic[A]):
2304+
2305+
def __init__(self) -> None: pass
2306+
2307+
@abstractmethod
2308+
def __call__(self, b: B) -> TwoTypes[A, B]: pass
2309+
2310+
class MakeTwoConcrete(Generic[A]):
2311+
2312+
def __call__(self, b: B) -> TwoTypes[A, B]: pass
2313+
2314+
2315+
class MakeTwoGenericSubAbstract(Generic[C], MakeTwoAbstract[C]):
2316+
2317+
def __call__(self, b: B) -> TwoTypes[C, B]: pass
2318+
2319+
class MakeTwoAppliedSubAbstract(MakeTwoAbstract[str]):
2320+
2321+
def __call__(self, b: B) -> TwoTypes[str, B]: pass
2322+
2323+
class Test():
2324+
2325+
def make_two(self,
2326+
mts: MakeTwoAbstract[A],
2327+
mte: MakeTwoConcrete[A],
2328+
mtgsa: MakeTwoGenericSubAbstract[A],
2329+
mtasa: MakeTwoAppliedSubAbstract) -> None:
2330+
reveal_type(mts(2)) # N: Revealed type is '__main__.TwoTypes[A`-1, builtins.int*]'
2331+
reveal_type(mte(2)) # N: Revealed type is '__main__.TwoTypes[A`-1, builtins.int*]'
2332+
reveal_type(mtgsa(2)) # N: Revealed type is '__main__.TwoTypes[A`-1, builtins.int*]'
2333+
reveal_type(mtasa(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]'
2334+
reveal_type(MakeTwoConcrete[int]()('foo')) # N: Revealed type is '__main__.TwoTypes[builtins.int, builtins.str*]'
2335+
reveal_type(MakeTwoConcrete[str]()(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]'
2336+
reveal_type(MakeTwoAppliedSubAbstract()('foo')) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.str*]'
2337+
reveal_type(MakeTwoAppliedSubAbstract()(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]'
2338+
reveal_type(MakeTwoGenericSubAbstract[str]()('foo')) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.str*]'
2339+
reveal_type(MakeTwoGenericSubAbstract[str]()(2)) # N: Revealed type is '__main__.TwoTypes[builtins.str, builtins.int*]'
2340+

test-data/unit/check-selftype.test

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,13 +468,13 @@ class B(A[Q]):
468468
a: A[int]
469469
b: B[str]
470470
reveal_type(a.g) # N: Revealed type is 'builtins.int'
471-
reveal_type(a.gt) # N: Revealed type is 'builtins.int*'
471+
reveal_type(a.gt) # N: Revealed type is 'builtins.int'
472472
reveal_type(a.f()) # N: Revealed type is 'builtins.int'
473-
reveal_type(a.ft()) # N: Revealed type is '__main__.A*[builtins.int]'
473+
reveal_type(a.ft()) # N: Revealed type is '__main__.A[builtins.int]'
474474
reveal_type(b.g) # N: Revealed type is 'builtins.int'
475-
reveal_type(b.gt) # N: Revealed type is 'builtins.str*'
475+
reveal_type(b.gt) # N: Revealed type is 'builtins.str'
476476
reveal_type(b.f()) # N: Revealed type is 'builtins.int'
477-
reveal_type(b.ft()) # N: Revealed type is '__main__.B*[builtins.str]'
477+
reveal_type(b.ft()) # N: Revealed type is '__main__.B[builtins.str]'
478478
[builtins fixtures/property.pyi]
479479

480480
[case testSelfTypeRestrictedMethod]

test-data/unit/lib-stub/abc.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ from typing import Type, Any, TypeVar
22

33
T = TypeVar('T', bound=Type[Any])
44

5+
class ABC(type): pass
56
class ABCMeta(type):
67
def register(cls, tp: T) -> T: pass
78
abstractmethod = object()

0 commit comments

Comments
 (0)