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 _process_kinded_type(kind: Instance) -> Type: """Recursively process all type arguments in a kind.""" if not kind.args: return kind real_type = get_proper_type(kind.args[0]) if isinstance(real_type, TypeVarType): return erase_to_bound(real_type) elif isinstance(real_type, Instance): return real_type.copy_modified(args=kind.args[1:len(real_type.args) + 1], ) # This should never happen, probably can be an exception: return AnyType(TypeOfAny.implementation_artifact)
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)
def debound(ctx: FunctionContext) -> MypyType: """ Analyzes the proper return type of ``debound`` function. Uses the ``upper_bound`` of ``TypeVar`` to infer the type of a ``Kind``. Here's a quick example: .. code:: python from typing import TypeVar from returns.primitives.hkt import Kind1, debound from returns.interfaces import Bindable1 B = TypeVar('B', bound=Bindable1) x: Kind1[B, int] instance, rebound = debound(x) reveal_type(instance) # Revealed type: 'Bindable[int]' See :func:`returns.primitives.hkt.debound` for more information. """ if not isinstance(ctx.default_return_type, TupleType): return AnyType(TypeOfAny.from_error) if not ctx.arg_types or not ctx.arg_types[0][0]: return AnyType(TypeOfAny.from_error) kind = cast(Instance, ctx.arg_types[0][0]) typevar = kind.args[0] if not isinstance(typevar, TypeVarType): ctx.api.fail(_KindErrors.debound_not_typevar, ctx.context) return AnyType(TypeOfAny.from_error) bound = get_proper_type(erase_to_bound(typevar)) if not isinstance(bound, Instance): ctx.api.fail(_KindErrors.debound_not_typevar, ctx.context) return AnyType(TypeOfAny.from_error) instance = bound.copy_modified(args=kind.args[1:]) return ctx.default_return_type.copy_modified( items=[instance, ctx.default_return_type.items[1]], )
def _process_kinded_type(kind: MypyType) -> MypyType: """Recursively process all type arguments in a kind.""" kind = get_proper_type(kind) if not isinstance(kind, Instance) or not kind.args: return kind real_type = kind.args[0] if isinstance(real_type, TypeVarType): return erase_to_bound(real_type) real_type = get_proper_type(real_type) if isinstance(real_type, Instance): return real_type.copy_modified(args=[ # Let's check if there are any nested `KindN[]` instance, # if so, it would be dekinded into a regular type following # the same rules: _process_kinded_type(type_arg) for type_arg in kind.args[1:len(real_type.args) + 1] ]) return kind
def _process_kinded_type(instance: MypyType) -> MypyType: """Recursively process all type arguments in a kind.""" kind = get_proper_type(instance) if not isinstance(kind, Instance) or not kind.args: return instance if kind.type.fullname != TYPED_KINDN: # this is some other instance return instance real_type = get_proper_type(kind.args[0]) if isinstance(real_type, TypeVarType): return erase_to_bound(real_type) elif isinstance(real_type, Instance): return real_type.copy_modified(args=[ # Let's check if there are any nested `KindN[]` instance, # if so, it would be dekinded into a regular type following # the same rules: _process_kinded_type(type_arg) for type_arg in kind.args[1:len(real_type.args) + 1] ]) # This should never happen, probably can be an exception: return AnyType(TypeOfAny.implementation_artifact)