From 023219bd6e19bf77be963138a6e8c10f5fb72ea5 Mon Sep 17 00:00:00 2001 From: elazar Date: Tue, 26 Sep 2017 22:21:39 +0300 Subject: [PATCH 1/9] Fix handling of non-method callable attribute (#3223) 1. Reverse subtyping check for self argument 2. For classmethod, fallback on the argument instead of on the parameter 3. Mimic dispatch better: meet original_type with static class 4. Better error message --- mypy/checkmember.py | 41 ++++++++++++----------------- mypy/messages.py | 10 +++---- test-data/unit/check-classes.test | 2 +- test-data/unit/check-functions.test | 6 ++--- test-data/unit/check-selftype.test | 31 +++++++++++++++++++--- 5 files changed, 53 insertions(+), 37 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index ea8aff82d209..8dcf272f375b 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -20,6 +20,8 @@ from mypy.plugin import Plugin, AttributeContext from mypy import messages from mypy import subtypes +from mypy import meet + MYPY = False if MYPY: # import for forward declaration only import mypy.checker @@ -314,7 +316,9 @@ def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, node: Cont # methods: the former to the instance, the latter to the # class. functype = t - check_method_type(functype, itype, var.is_classmethod, node, msg) + # Use meet to simulate dispatch - e.g. reduce Union[A, B] to A on dispatch to A + dispatched_type = meet.meet_types(original_type, itype) + check_self_arg(functype, dispatched_type, var.is_classmethod, node, name, msg) signature = bind_self(functype, original_type, var.is_classmethod) if var.is_property: # A property cannot have an overloaded type => the cast @@ -370,33 +374,22 @@ def lookup_member_var_or_accessor(info: TypeInfo, name: str, return None -def check_method_type(functype: FunctionLike, itype: Instance, is_classmethod: bool, - context: Context, msg: MessageBuilder) -> None: +def check_self_arg(functype: FunctionLike, original_type: Type, is_classmethod: bool, + context: Context, name: str, msg: MessageBuilder) -> None: + """Check that the the most precise type of the self argument is compatible + with the declared type of each of the overloads. + """ for item in functype.items(): if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR): # No positional first (self) argument (*args is okay). - msg.invalid_method_type(item, context) - elif not is_classmethod: - # Check that self argument has type 'Any' or valid instance type. - selfarg = item.arg_types[0] - # If this is a method of a tuple class, correct for the fact that - # we passed to typ.fallback in analyze_member_access. See #1432. - if isinstance(selfarg, TupleType): - selfarg = selfarg.fallback - if not subtypes.is_subtype(selfarg, itype): - msg.invalid_method_type(item, context) + msg.fail('Attribute function with type %s does not accept self argument' + % msg.format(item), context) else: - # Check that cls argument has type 'Any' or valid class type. - # (This is sufficient for the current treatment of @classmethod, - # but probably needs to be revisited when we implement Type[C] - # or advanced variants of it like Type[, C].) - clsarg = item.arg_types[0] - if isinstance(clsarg, CallableType) and clsarg.is_type_obj(): - if not subtypes.is_equivalent(clsarg.ret_type, itype): - msg.invalid_class_method_type(item, context) - else: - if not subtypes.is_equivalent(clsarg, AnyType(TypeOfAny.special_form)): - msg.invalid_class_method_type(item, context) + selfarg = item.arg_types[0] + if is_classmethod: + original_type = TypeType.make_normalized(original_type) + if not subtypes.is_subtype(original_type, erase_to_bound(selfarg)): + msg.invalid_method_type(name, original_type, item, is_classmethod, context) def analyze_class_attribute_access(itype: Instance, diff --git a/mypy/messages.py b/mypy/messages.py index 2f3473b3e59d..1f25f1990d2a 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -856,11 +856,11 @@ def cannot_determine_type(self, name: str, context: Context) -> None: def cannot_determine_type_in_base(self, name: str, base: str, context: Context) -> None: self.fail("Cannot determine type of '%s' in base class '%s'" % (name, base), context) - def invalid_method_type(self, sig: CallableType, context: Context) -> None: - self.fail('Invalid method type', context) - - def invalid_class_method_type(self, sig: CallableType, context: Context) -> None: - self.fail('Invalid class method type', context) + def invalid_method_type(self, name: str, arg: Type, sig: CallableType, is_classmethod: bool, + context: Context) -> None: + kind = 'class attribute function' if is_classmethod else 'attribute function' + self.fail('Invalid self argument %s to %s "%s" with type %s' + % (self.format(arg), kind, name, self.format(sig)), context) def incompatible_conditional_function_def(self, defn: FuncDef) -> None: self.fail('All conditional function variants must have identical ' diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 2ff0b8d84d78..6f299d5c5647 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2105,7 +2105,7 @@ class B: a = A bad = lambda: 42 -B().bad() # E: Invalid method type +B().bad() # E: Attribute function with type "Callable[[], int]" does not accept self argument reveal_type(B.a) # E: Revealed type is 'def () -> __main__.A' reveal_type(B().a) # E: Revealed type is 'def () -> __main__.A' reveal_type(B().a()) # E: Revealed type is '__main__.A' diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index e39606251f9d..f6f3d7a971b6 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -507,8 +507,8 @@ class A: f = x # type: Callable[[], None] g = x # type: Callable[[B], None] a = None # type: A -a.f() # E: Invalid method type -a.g() # E: Invalid method type +a.f() # E: Attribute function with type "Callable[[], None]" does not accept self argument +a.g() # E: Invalid self argument "A" to attribute function "g" with type "Callable[[B], None]" [case testMethodWithDynamicallyTypedMethodAsDataAttribute] from typing import Any, Callable @@ -568,7 +568,7 @@ class A(Generic[t]): ab = None # type: A[B] ac = None # type: A[C] ab.f() -ac.f() # E: Invalid method type +ac.f() # E: Invalid self argument "A[C]" to attribute function "f" with type "Callable[[A[B]], None]" [case testPartiallyTypedSelfInMethodDataAttribute] from typing import Any, TypeVar, Generic, Callable diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index d0c4a56f2038..d32b74261371 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -354,16 +354,21 @@ class E: [case testSelfTypeProperty] from typing import TypeVar -T = TypeVar('T', bound='A') +Q = TypeVar('Q') +T = TypeVar('T', bound='X') -class A: +class X: @property - def member(self: T) -> T: - pass + def __members__(self: Q) -> Q: return self + +class A(X): + @property + def member(self: T) -> T: return self class B(A): pass +reveal_type(X().__members__) # E: Revealed type is '__main__.X*' reveal_type(A().member) # E: Revealed type is '__main__.A*' reveal_type(B().member) # E: Revealed type is '__main__.B*' @@ -376,3 +381,21 @@ class A: # def g(self: None) -> None: ... see in check-python2.test [out] main:3: error: Self argument missing for a non-static method (or an invalid type for self) + +[case testUnionPropertyField] +from typing import Union + +class A: + x: int + +class B: + @property + def x(self) -> int: return 1 + +class C: + @property + def x(self) -> int: return 1 + +ab: Union[A, B, C] +reveal_type(ab.x) # E: Revealed type is 'builtins.int' +[builtins fixtures/property.pyi] From 295d3ff76ce82dadb051af43d73533fc885ba3d9 Mon Sep 17 00:00:00 2001 From: elazar Date: Wed, 11 Oct 2017 02:11:27 +0300 Subject: [PATCH 2/9] elaborate dispatch --- mypy/checkmember.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 8dcf272f375b..fffeb42e03e9 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -288,6 +288,7 @@ def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, node: Cont This is conceptually part of analyze_member_access and the arguments are similar. + itype is the class object in which var is dedined original_type is the type of E in the expression E.var """ # Found a member variable. @@ -312,17 +313,21 @@ def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, node: Cont msg.cant_assign_to_method(node) if not var.is_staticmethod: - # Class-level function objects and classmethods become bound - # methods: the former to the instance, the latter to the - # class. + # Class-level function objects and classmethods become bound methods: + # the former to the instance, the latter to the class. functype = t - # Use meet to simulate dispatch - e.g. reduce Union[A, B] to A on dispatch to A + # Use meet to narrow original_type to the dispatched type. + # For example, assume + # * A.f: Callable[[A1], None] where A1 <: A (maybe A1 == A) + # * B.f: Callable[[B1], None] where B1 <: B (maybe B1 == B) + # * x: Union[A1, B1] + # 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(original_type, itype) check_self_arg(functype, dispatched_type, var.is_classmethod, node, name, msg) signature = bind_self(functype, original_type, var.is_classmethod) if var.is_property: - # A property cannot have an overloaded type => the cast - # is fine. + # A property cannot have an overloaded type => the cast is fine. assert isinstance(signature, CallableType) result = signature.ret_type else: From abd54260af25d5e1b6fb355546cb8f28a5b03ad2 Mon Sep 17 00:00:00 2001 From: elazar Date: Wed, 11 Oct 2017 02:36:30 +0300 Subject: [PATCH 3/9] try to elaborate docstring --- mypy/checkmember.py | 18 ++++++++++++------ mypy/messages.py | 4 ++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index fffeb42e03e9..a0eab94da9e4 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -379,11 +379,17 @@ def lookup_member_var_or_accessor(info: TypeInfo, name: str, return None -def check_self_arg(functype: FunctionLike, original_type: Type, is_classmethod: bool, +def check_self_arg(functype: FunctionLike, dispatched_arg_type: Type, is_classmethod: bool, context: Context, name: str, msg: MessageBuilder) -> None: - """Check that the the most precise type of the self argument is compatible - with the declared type of each of the overloads. + """For x.f where A.f: A1 -> T, check that meet(type(x), A) <: A1 for each overload. + + dispatched_arg_type is meet(B, A) in the following example + class A: + f: Callable[[A1], None] + + def g(x: B): x.f """ + # TODO: this is too strict. We can return filtered overloads for matching definitions for item in functype.items(): if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR): # No positional first (self) argument (*args is okay). @@ -392,9 +398,9 @@ def check_self_arg(functype: FunctionLike, original_type: Type, is_classmethod: else: selfarg = item.arg_types[0] if is_classmethod: - original_type = TypeType.make_normalized(original_type) - if not subtypes.is_subtype(original_type, erase_to_bound(selfarg)): - msg.invalid_method_type(name, original_type, item, is_classmethod, context) + dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type) + if not subtypes.is_subtype(dispatched_arg_type, erase_to_bound(selfarg)): + msg.incompatible_self_argument(name, dispatched_arg_type, item, is_classmethod, context) def analyze_class_attribute_access(itype: Instance, diff --git a/mypy/messages.py b/mypy/messages.py index dc3409043bda..69cd5d1cf3a7 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -863,8 +863,8 @@ def cannot_determine_type(self, name: str, context: Context) -> None: def cannot_determine_type_in_base(self, name: str, base: str, context: Context) -> None: self.fail("Cannot determine type of '%s' in base class '%s'" % (name, base), context) - def invalid_method_type(self, name: str, arg: Type, sig: CallableType, is_classmethod: bool, - context: Context) -> None: + def incompatible_self_argument(self, name: str, arg: Type, sig: CallableType, + is_classmethod: bool, context: Context) -> None: kind = 'class attribute function' if is_classmethod else 'attribute function' self.fail('Invalid self argument %s to %s "%s" with type %s' % (self.format(arg), kind, name, self.format(sig)), context) From 3d941a572c744eac20504a5246a73ddfccfbbc1a Mon Sep 17 00:00:00 2001 From: elazar Date: Wed, 11 Oct 2017 02:52:24 +0300 Subject: [PATCH 4/9] move and improve error message for 0-arg attr function --- mypy/checkmember.py | 3 +-- mypy/messages.py | 4 ++++ test-data/unit/check-classes.test | 2 +- test-data/unit/check-functions.test | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index a0eab94da9e4..edc419fc7d2c 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -393,8 +393,7 @@ def g(x: B): x.f for item in functype.items(): if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR): # No positional first (self) argument (*args is okay). - msg.fail('Attribute function with type %s does not accept self argument' - % msg.format(item), context) + msg.no_formal_self(name, item, context) else: selfarg = item.arg_types[0] if is_classmethod: diff --git a/mypy/messages.py b/mypy/messages.py index 69cd5d1cf3a7..b49292e735f3 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -863,6 +863,10 @@ def cannot_determine_type(self, name: str, context: Context) -> None: def cannot_determine_type_in_base(self, name: str, base: str, context: Context) -> None: self.fail("Cannot determine type of '%s' in base class '%s'" % (name, base), context) + def no_formal_self(self, name: str, item: CallableType, context: Context) -> None: + self.fail('Attribute function "%s" with type %s does not accept self argument' + % (name, self.format(item)), context) + def incompatible_self_argument(self, name: str, arg: Type, sig: CallableType, is_classmethod: bool, context: Context) -> None: kind = 'class attribute function' if is_classmethod else 'attribute function' diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index dd03a286eed2..7f72491983a0 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2105,7 +2105,7 @@ class B: a = A bad = lambda: 42 -B().bad() # E: Attribute function with type "Callable[[], int]" does not accept self argument +B().bad() # E: Attribute function "bad" with type "Callable[[], int]" does not accept self argument reveal_type(B.a) # E: Revealed type is 'def () -> __main__.A' reveal_type(B().a) # E: Revealed type is 'def () -> __main__.A' reveal_type(B().a()) # E: Revealed type is '__main__.A' diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index f6f3d7a971b6..5e99613eeba2 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -507,7 +507,7 @@ class A: f = x # type: Callable[[], None] g = x # type: Callable[[B], None] a = None # type: A -a.f() # E: Attribute function with type "Callable[[], None]" does not accept self argument +a.f() # E: Attribute function "f" with type "Callable[[], None]" does not accept self argument a.g() # E: Invalid self argument "A" to attribute function "g" with type "Callable[[B], None]" [case testMethodWithDynamicallyTypedMethodAsDataAttribute] From 46abe18e2b50959cacce4ad40e4b8811eb2b5ec8 Mon Sep 17 00:00:00 2001 From: elazar Date: Wed, 11 Oct 2017 03:15:39 +0300 Subject: [PATCH 5/9] add and improve tests: Tuple and Meta --- mypy/checkmember.py | 7 +-- test-data/unit/check-selftype.test | 81 ++++++++++++++++++++++++++---- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/mypy/checkmember.py b/mypy/checkmember.py index edc419fc7d2c..93cd1e0541ab 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -384,10 +384,10 @@ def check_self_arg(functype: FunctionLike, dispatched_arg_type: Type, is_classme """For x.f where A.f: A1 -> T, check that meet(type(x), A) <: A1 for each overload. dispatched_arg_type is meet(B, A) in the following example - class A: - f: Callable[[A1], None] def g(x: B): x.f + class A: + f: Callable[[A1], None] """ # TODO: this is too strict. We can return filtered overloads for matching definitions for item in functype.items(): @@ -399,7 +399,8 @@ def g(x: B): x.f if is_classmethod: dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type) if not subtypes.is_subtype(dispatched_arg_type, erase_to_bound(selfarg)): - msg.incompatible_self_argument(name, dispatched_arg_type, item, is_classmethod, context) + msg.incompatible_self_argument(name, dispatched_arg_type, item, + is_classmethod, context) def analyze_class_attribute_access(itype: Instance, diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index d32b74261371..f37ce4e80ad8 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -351,26 +351,85 @@ class E: def __init_subclass__(cls) -> None: reveal_type(cls) # E: Revealed type is 'def () -> __main__.E' -[case testSelfTypeProperty] -from typing import TypeVar +[case testSelfTypeProperSupertypeAttribute] +from typing import Callable, TypeVar +T = TypeVar('T', bound=object) +class A: + @property + def g(self: object) -> int: return 0 + @property + def gt(self: T) -> T: return self + f: Callable[[object], int] + ft: Callable[[T], T] -Q = TypeVar('Q') -T = TypeVar('T', bound='X') +class B(A): + pass + +reveal_type(A().g) # E: Revealed type is 'builtins.int' +reveal_type(A().gt) # E: Revealed type is '__main__.A*' +reveal_type(A().f()) # E: Revealed type is 'builtins.int' +reveal_type(A().ft()) # E: Revealed type is '__main__.A*' +reveal_type(B().g) # E: Revealed type is 'builtins.int' +reveal_type(B().gt) # E: Revealed type is '__main__.B*' +reveal_type(B().f()) # E: Revealed type is 'builtins.int' +reveal_type(B().ft()) # E: Revealed type is '__main__.B*' + +[builtins fixtures/property.pyi] -class X: +[case testSelfTypeProperSupertypeAttributeTuple] +from typing import Callable, TypeVar, Tuple +T = TypeVar('T', bound=object) +class A(Tuple[int, int]): @property - def __members__(self: Q) -> Q: return self + def g(self: object) -> int: return 0 + @property + def gt(self: T) -> T: return self + f: Callable[[object], int] + ft: Callable[[T], T] + +class B(A): + pass -class A(X): +reveal_type(A().g) # E: Revealed type is 'builtins.int' +reveal_type(A().gt) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fallback=__main__.A]' +reveal_type(A().f()) # E: Revealed type is 'builtins.int' +reveal_type(A().ft()) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fallback=__main__.A]' +reveal_type(B().g) # E: Revealed type is 'builtins.int' +reveal_type(B().gt) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fallback=__main__.B]' +reveal_type(B().f()) # E: Revealed type is 'builtins.int' +reveal_type(B().ft()) # E: Revealed type is 'Tuple[builtins.int, builtins.int, fallback=__main__.B]' + +[builtins fixtures/property.pyi] + +[case testSelfTypeProperSupertypeAttributeMeta] +from typing import Callable, TypeVar, Type +T = TypeVar('T', bound=object) +class A(type): + @property + def g(cls: object) -> int: return 0 @property - def member(self: T) -> T: return self + def gt(cls: T) -> T: return cls + f: Callable[[object], int] + ft: Callable[[T], T] class B(A): pass -reveal_type(X().__members__) # E: Revealed type is '__main__.X*' -reveal_type(A().member) # E: Revealed type is '__main__.A*' -reveal_type(B().member) # E: Revealed type is '__main__.B*' +class X(metaclass=B): pass +class Y(X): pass +X1: Type[X] +reveal_type(X.g) # E: Revealed type is 'builtins.int' +reveal_type(X.gt) # E: Revealed type is 'def () -> __main__.X' +reveal_type(X.f()) # E: Revealed type is 'builtins.int' +reveal_type(X.ft()) # E: Revealed type is 'def () -> __main__.X' +reveal_type(Y.g) # E: Revealed type is 'builtins.int' +reveal_type(Y.gt) # E: Revealed type is 'def () -> __main__.Y' +reveal_type(Y.f()) # E: Revealed type is 'builtins.int' +reveal_type(Y.ft()) # E: Revealed type is 'def () -> __main__.Y' +reveal_type(X1.g) # E: Revealed type is 'builtins.int' +reveal_type(X1.gt) # E: Revealed type is 'Type[__main__.X]' +reveal_type(X1.f()) # E: Revealed type is 'builtins.int' +reveal_type(X1.ft()) # E: Revealed type is 'Type[__main__.X]' [builtins fixtures/property.pyi] From f2c6d3215016526d8f85add7ecce5a199fb1a3b9 Mon Sep 17 00:00:00 2001 From: elazar Date: Wed, 11 Oct 2017 03:38:07 +0300 Subject: [PATCH 6/9] demonstrate the need for meet --- test-data/unit/check-selftype.test | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index f37ce4e80ad8..14eb78c5c063 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -351,6 +351,20 @@ class E: def __init_subclass__(cls) -> None: reveal_type(cls) # E: Revealed type is 'def () -> __main__.E' +[case testSelfTypePropertyUnion] +from typing import Union +class A: + @property + def f(self: A) -> int: pass + +class B: + @property + def f(self: B) -> int: pass +x: Union[A, B] +reveal_type(x.f) # E: Revealed type is 'builtins.int' + +[builtins fixtures/property.pyi] + [case testSelfTypeProperSupertypeAttribute] from typing import Callable, TypeVar T = TypeVar('T', bound=object) From fae22afcba4dd1a6ce5e3a1630385f90a1279071 Mon Sep 17 00:00:00 2001 From: elazar Date: Wed, 11 Oct 2017 16:21:43 +0300 Subject: [PATCH 7/9] test for non-object bound; add explicit __init__ --- test-data/unit/check-selftype.test | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 14eb78c5c063..5e7c09b7a5be 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -367,10 +367,11 @@ reveal_type(x.f) # E: Revealed type is 'builtins.int' [case testSelfTypeProperSupertypeAttribute] from typing import Callable, TypeVar -T = TypeVar('T', bound=object) -class A: +class K: pass +T = TypeVar('T', bound=K) +class A(K): @property - def g(self: object) -> int: return 0 + def g(self: K) -> int: return 0 @property def gt(self: T) -> T: return self f: Callable[[object], int] @@ -392,7 +393,7 @@ reveal_type(B().ft()) # E: Revealed type is '__main__.B*' [case testSelfTypeProperSupertypeAttributeTuple] from typing import Callable, TypeVar, Tuple -T = TypeVar('T', bound=object) +T = TypeVar('T') class A(Tuple[int, int]): @property def g(self: object) -> int: return 0 @@ -417,7 +418,7 @@ reveal_type(B().ft()) # E: Revealed type is 'Tuple[builtins.int, builtins.int, [case testSelfTypeProperSupertypeAttributeMeta] from typing import Callable, TypeVar, Type -T = TypeVar('T', bound=object) +T = TypeVar('T') class A(type): @property def g(cls: object) -> int: return 0 @@ -429,17 +430,18 @@ class A(type): class B(A): pass -class X(metaclass=B): pass +class X(metaclass=B): + def __init__(self, x: int) -> None: pass class Y(X): pass X1: Type[X] reveal_type(X.g) # E: Revealed type is 'builtins.int' -reveal_type(X.gt) # E: Revealed type is 'def () -> __main__.X' +reveal_type(X.gt) # E: Revealed type is 'def (x: builtins.int) -> __main__.X' reveal_type(X.f()) # E: Revealed type is 'builtins.int' -reveal_type(X.ft()) # E: Revealed type is 'def () -> __main__.X' +reveal_type(X.ft()) # E: Revealed type is 'def (x: builtins.int) -> __main__.X' reveal_type(Y.g) # E: Revealed type is 'builtins.int' -reveal_type(Y.gt) # E: Revealed type is 'def () -> __main__.Y' +reveal_type(Y.gt) # E: Revealed type is 'def (x: builtins.int) -> __main__.Y' reveal_type(Y.f()) # E: Revealed type is 'builtins.int' -reveal_type(Y.ft()) # E: Revealed type is 'def () -> __main__.Y' +reveal_type(Y.ft()) # E: Revealed type is 'def (x: builtins.int) -> __main__.Y' reveal_type(X1.g) # E: Revealed type is 'builtins.int' reveal_type(X1.gt) # E: Revealed type is 'Type[__main__.X]' reveal_type(X1.f()) # E: Revealed type is 'builtins.int' From a9897754686952ceb484b71c50d34d38b6d33675 Mon Sep 17 00:00:00 2001 From: elazar Date: Wed, 11 Oct 2017 17:44:17 +0300 Subject: [PATCH 8/9] add test for generic instantiation --- test-data/unit/check-selftype.test | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index 5e7c09b7a5be..bbe0a15f5b0c 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -449,6 +449,35 @@ reveal_type(X1.ft()) # E: Revealed type is 'Type[__main__.X]' [builtins fixtures/property.pyi] +[case testSelfTypeProperSupertypeAttributeGeneric] +from typing import Callable, TypeVar, Generic +Q = TypeVar('Q', covariant=True) +class K(Generic[Q]): + q: Q +T = TypeVar('T') +class A(K[Q]): + @property + def g(self: K[object]) -> int: return 0 + @property + def gt(self: K[T]) -> T: return self.q + f: Callable[[object], int] + ft: Callable[[T], T] + +class B(A[Q]): + pass +a: A[int] +b: B[str] +reveal_type(a.g) # E: Revealed type is 'builtins.int' +--reveal_type(a.gt) # E: Revealed type is 'builtins.int' +reveal_type(a.f()) # E: Revealed type is 'builtins.int' +--reveal_type(a.ft()) # E: Revealed type is '__main__.A*[builtins.int]' +reveal_type(b.g) # E: Revealed type is 'builtins.int' +--reveal_type(b.gt) # E: Revealed type is '__main__.B*[builtins.str]' +reveal_type(b.f()) # E: Revealed type is 'builtins.int' +--reveal_type(b.ft()) # E: Revealed type is '__main__.B*[builtins.str]' + +[builtins fixtures/property.pyi] + [case testSelfTypeNotSelfType] # Friendlier error messages for common mistakes. See #2950 class A: From eb74f853fd14d4cb5dad2a25c99877ed917e85fb Mon Sep 17 00:00:00 2001 From: elazar Date: Wed, 11 Oct 2017 17:53:53 +0300 Subject: [PATCH 9/9] remove commmenting of two lines --- test-data/unit/check-selftype.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index bbe0a15f5b0c..4d79b9272026 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -470,11 +470,11 @@ b: B[str] reveal_type(a.g) # E: Revealed type is 'builtins.int' --reveal_type(a.gt) # E: Revealed type is 'builtins.int' reveal_type(a.f()) # E: Revealed type is 'builtins.int' ---reveal_type(a.ft()) # E: Revealed type is '__main__.A*[builtins.int]' +reveal_type(a.ft()) # E: Revealed type is '__main__.A*[builtins.int]' reveal_type(b.g) # E: Revealed type is 'builtins.int' --reveal_type(b.gt) # E: Revealed type is '__main__.B*[builtins.str]' reveal_type(b.f()) # E: Revealed type is 'builtins.int' ---reveal_type(b.ft()) # E: Revealed type is '__main__.B*[builtins.str]' +reveal_type(b.ft()) # E: Revealed type is '__main__.B*[builtins.str]' [builtins fixtures/property.pyi]