def check_self_arg(functype: FunctionLike, dispatched_arg_type: Type, is_classmethod: bool, context: Context, name: str, msg: MessageBuilder) -> None: """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 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(): 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.no_formal_self(name, item, context) else: selfarg = item.arg_types[0] 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)
def check_self_arg(functype: FunctionLike, dispatched_arg_type: Type, is_classmethod: bool, context: Context, name: str, msg: MessageBuilder) -> None: """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 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(): 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.no_formal_self(name, item, context) else: selfarg = item.arg_types[0] 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)
def check_self_arg(functype: FunctionLike, dispatched_arg_type: Type, is_classmethod: bool, context: Context, name: str, msg: MessageBuilder) -> FunctionLike: """Check that an instance has a valid type for a method with annotated 'self'. For example if the method is defined as: class A: def f(self: S) -> T: ... then for 'x.f' we check that meet(type(x), A) <: S. If the method is overloaded, we select only overloads items that satisfy this requirement. If there are no matching overloads, an error is generated. Note: dispatched_arg_type uses a meet to select a relevant item in case if the original type of 'x' is a union. This is done because several special methods treat union types in ad-hoc manner, so we can't use MemberContext.self_type yet. """ items = functype.items if not items: return functype new_items = [] if is_classmethod: dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type) for item in 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.no_formal_self(name, item, context) # This is pretty bad, so just return the original signature if # there is at least one such error. return functype else: selfarg = item.arg_types[0] if subtypes.is_subtype(dispatched_arg_type, erase_typevars(erase_to_bound(selfarg))): new_items.append(item) elif isinstance(selfarg, ParamSpecType): # TODO: This is not always right. What's the most reasonable thing to do here? new_items.append(item) if not new_items: # Choose first item for the message (it may be not very helpful for overloads). msg.incompatible_self_argument(name, dispatched_arg_type, items[0], is_classmethod, context) return functype if len(new_items) == 1: return new_items[0] return Overloaded(new_items)