def bind_self(method: F, original_type: Optional[Type] = None, is_classmethod: bool = False) -> F: """Return a copy of `method`, with the type of its first parameter (usually self or cls) bound to original_type. If the type of `self` is a generic type (T, or Type[T] for classmethods), instantiate every occurrence of type with original_type in the rest of the signature and in the return type. original_type is the type of E in the expression E.copy(). It is None in compatibility checks. In this case we treat it as the erasure of the declared type of self. This way we can express "the type of self". For example: T = TypeVar('T', bound='A') class A: def copy(self: T) -> T: ... class B(A): pass b = B().copy() # type: B """ from mypy.infer import infer_type_arguments if isinstance(method, Overloaded): return cast( F, Overloaded([ bind_self(c, original_type, is_classmethod) for c in method.items ])) assert isinstance(method, CallableType) func = method if not func.arg_types: # Invalid method, return something. return cast(F, func) if func.arg_kinds[0] == ARG_STAR: # The signature is of the form 'def foo(*args, ...)'. # In this case we shouldn't drop the first arg, # since func will be absorbed by the *args. # TODO: infer bounds on the type of *args? return cast(F, func) self_param_type = get_proper_type(func.arg_types[0]) variables: Sequence[TypeVarLikeType] = [] if func.variables and supported_self_type(self_param_type): if original_type is None: # TODO: type check method override (see #7861). original_type = erase_to_bound(self_param_type) original_type = get_proper_type(original_type) all_ids = func.type_var_ids() typeargs = infer_type_arguments(all_ids, self_param_type, original_type, is_supertype=True) if (is_classmethod # TODO: why do we need the extra guards here? and any( isinstance(get_proper_type(t), UninhabitedType) for t in typeargs) and isinstance( original_type, (Instance, TypeVarType, TupleType))): # In case we call a classmethod through an instance x, fallback to type(x) typeargs = infer_type_arguments(all_ids, self_param_type, TypeType(original_type), is_supertype=True) ids = [ tid for tid in all_ids if any(tid == t.id for t in get_type_vars(self_param_type)) ] # Technically, some constrains might be unsolvable, make them <nothing>. to_apply = [ t if t is not None else UninhabitedType() for t in typeargs ] def expand(target: Type) -> Type: return expand_type(target, {id: to_apply[all_ids.index(id)] for id in ids}) arg_types = [expand(x) for x in func.arg_types[1:]] ret_type = expand(func.ret_type) variables = [v for v in func.variables if v.id not in ids] else: arg_types = func.arg_types[1:] ret_type = func.ret_type variables = func.variables original_type = get_proper_type(original_type) if isinstance(original_type, CallableType) and original_type.is_type_obj(): original_type = TypeType.make_normalized(original_type.ret_type) res = func.copy_modified(arg_types=arg_types, arg_kinds=func.arg_kinds[1:], arg_names=func.arg_names[1:], variables=variables, ret_type=ret_type, bound_args=[original_type]) return cast(F, res)
def bind_self(method: F, original_type: Optional[Type] = None, is_classmethod: bool = False) -> F: """Return a copy of `method`, with the type of its first parameter (usually self or cls) bound to original_type. If the type of `self` is a generic type (T, or Type[T] for classmethods), instantiate every occurrence of type with original_type in the rest of the signature and in the return type. original_type is the type of E in the expression E.copy(). It is None in compatibility checks. In this case we treat it as the erasure of the declared type of self. This way we can express "the type of self". For example: T = TypeVar('T', bound='A') class A: def copy(self: T) -> T: ... class B(A): pass b = B().copy() # type: B """ if isinstance(method, Overloaded): return cast( F, Overloaded([bind_self(c, original_type) for c in method.items()])) assert isinstance(method, CallableType) func = method if not func.arg_types: # invalid method. return something return cast(F, func) if func.arg_kinds[0] == ARG_STAR: # The signature is of the form 'def foo(*args, ...)'. # In this case we shouldn't drop the first arg, # since func will be absorbed by the *args. # TODO: infer bounds on the type of *args? return cast(F, func) self_param_type = func.arg_types[0] if func.variables and (isinstance(self_param_type, TypeVarType) or (isinstance(self_param_type, TypeType) and isinstance(self_param_type.item, TypeVarType))): if original_type is None: # Type check method override # XXX value restriction as union? original_type = erase_to_bound(self_param_type) ids = [x.id for x in func.variables] typearg = infer_type_arguments(ids, self_param_type, original_type)[0] if (is_classmethod and isinstance(typearg, UninhabitedType) and isinstance(original_type, (Instance, TypeVarType, TupleType))): # In case we call a classmethod through an instance x, fallback to type(x) # TODO: handle Union typearg = infer_type_arguments(ids, self_param_type, TypeType(original_type))[0] def expand(target: Type) -> Type: assert typearg is not None return expand_type(target, {func.variables[0].id: typearg}) arg_types = [expand(x) for x in func.arg_types[1:]] ret_type = expand(func.ret_type) variables = func.variables[1:] else: arg_types = func.arg_types[1:] ret_type = func.ret_type variables = func.variables if isinstance(original_type, CallableType) and original_type.is_type_obj(): original_type = TypeType.make_normalized(original_type.ret_type) res = func.copy_modified(arg_types=arg_types, arg_kinds=func.arg_kinds[1:], arg_names=func.arg_names[1:], variables=variables, ret_type=ret_type, bound_args=[original_type]) return cast(F, res)
def bind_self(method: F, original_type: Type = None) -> F: """Return a copy of `method`, with the type of its first parameter (usually self or cls) bound to original_type. If the type of `self` is a generic type (T, or Type[T] for classmethods), instantiate every occurrence of type with original_type in the rest of the signature and in the return type. original_type is the type of E in the expression E.copy(). It is None in compatibility checks. In this case we treat it as the erasure of the declared type of self. This way we can express "the type of self". For example: T = TypeVar('T', bound='A') class A: def copy(self: T) -> T: ... class B(A): pass b = B().copy() # type: B """ if isinstance(method, Overloaded): return cast(F, Overloaded([bind_self(c, method) for c in method.items()])) assert isinstance(method, CallableType) func = method if not func.arg_types: # invalid method. return something return cast(F, func) if func.arg_kinds[0] == ARG_STAR: # The signature is of the form 'def foo(*args, ...)'. # In this case we shouldn't drop the first arg, # since func will be absorbed by the *args. # TODO: infer bounds on the type of *args? return cast(F, func) self_param_type = func.arg_types[0] if func.variables and (isinstance(self_param_type, TypeVarType) or (isinstance(self_param_type, TypeType) and isinstance(self_param_type.item, TypeVarType))): if original_type is None: # Type check method override # XXX value restriction as union? original_type = erase_to_bound(self_param_type) typearg = infer_type_arguments([x.id for x in func.variables], self_param_type, original_type)[0] def expand(target: Type) -> Type: return expand_type(target, {func.variables[0].id: typearg}) arg_types = [expand(x) for x in func.arg_types[1:]] ret_type = expand(func.ret_type) variables = func.variables[1:] else: arg_types = func.arg_types[1:] ret_type = func.ret_type variables = func.variables res = func.copy_modified(arg_types=arg_types, arg_kinds=func.arg_kinds[1:], arg_names=func.arg_names[1:], variables=variables, ret_type=ret_type) return cast(F, res)