def visit_callable_type(self, t: CallableType) -> Type: param_spec = t.param_spec() if param_spec is not None: repl = get_proper_type(self.variables.get(param_spec.id)) # If a ParamSpec in a callable type is substituted with a # callable type, we can't use normal substitution logic, # since ParamSpec is actually split into two components # *P.args and **P.kwargs in the original type. Instead, we # must expand both of them with all the argument types, # kinds and names in the replacement. The return type in # the replacement is ignored. if isinstance(repl, CallableType) or isinstance(repl, Parameters): # Substitute *args: P.args, **kwargs: P.kwargs prefix = param_spec.prefix # we need to expand the types in the prefix, so might as well # not get them in the first place t = t.expand_param_spec(repl, no_prefix=True) return t.copy_modified( arg_types=self.expand_types(prefix.arg_types) + t.arg_types, arg_kinds=prefix.arg_kinds + t.arg_kinds, arg_names=prefix.arg_names + t.arg_names, ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None)) return t.copy_modified(arg_types=self.expand_types(t.arg_types), ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None))
def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance, special_sig: Optional[str], is_new: bool, orig_self_type: Optional[Type] = None) -> CallableType: """Create a type object type based on the signature of __init__.""" variables: List[TypeVarLikeType] = [] variables.extend(info.defn.type_vars) variables.extend(init_type.variables) from mypy.subtypes import is_subtype init_ret_type = get_proper_type(init_type.ret_type) orig_self_type = get_proper_type(orig_self_type) default_ret_type = fill_typevars(info) explicit_type = init_ret_type if is_new else orig_self_type if (isinstance(explicit_type, (Instance, TupleType)) # Only use the declared return type from __new__ or declared self in __init__ # if it is actually returning a subtype of what we would return otherwise. and is_subtype( explicit_type, default_ret_type, ignore_type_params=True)): ret_type: Type = explicit_type else: ret_type = default_ret_type callable_type = init_type.copy_modified(ret_type=ret_type, fallback=type_type, name=None, variables=variables, special_sig=special_sig) c = callable_type.with_name(info.name) return c
def visit_callable_type(self, t: CallableType) -> Type: return t.copy_modified( arg_types=self.anal_array(t.arg_types), ret_type=t.ret_type.accept(self), fallback=t.fallback or self.builtin_type("builtins.function"), variables=self.anal_var_defs(t.variables), )
def visit_callable_type(self, t: CallableType) -> Type: return t.copy_modified(arg_types=self.anal_array(t.arg_types), ret_type=t.ret_type.accept(self), fallback=t.fallback or self.builtin_type('builtins.function'), variables=self.anal_var_defs(t.variables), bound_vars=self.anal_bound_vars(t.bound_vars))
def typed_dict_get_signature_callback( object_type: Type, args: List[List[Expression]], signature: CallableType, named_generic_type: Callable[[str, List[Type]], Type]) -> CallableType: """Try to infer a better signature type for TypedDict.get. This is used to get better type context for the second argument that depends on a TypedDict value type. """ if (isinstance(object_type, TypedDictType) and len(args) == 2 and len(args[0]) == 1 and isinstance(args[0][0], StrExpr) and len(signature.arg_types) == 2 and len(signature.variables) == 1): key = args[0][0].value value_type = object_type.items.get(key) if value_type: # Tweak the signature to include the value type as context. It's # only needed for type inference since there's a union with a type # variable that accepts everything. tv = TypeVarType(signature.variables[0]) return signature.copy_modified(arg_types=[ signature.arg_types[0], UnionType.make_simplified_union([value_type, tv]) ]) return signature
def apply_generic_arguments(callable: CallableType, types: List[Type], msg: MessageBuilder, context: Context) -> CallableType: """Apply generic type arguments to a callable type. For example, applying [int] to 'def [T] (T) -> T' results in 'def (int) -> int'. Note that each type can be None; in this case, it will not be applied. """ tvars = callable.variables assert len(tvars) == len(types) # Check that inferred type variable values are compatible with allowed # values and bounds. Also, promote subtype values to allowed values. types = types[:] for i, type in enumerate(types): values = callable.variables[i].values if values and type: if isinstance(type, AnyType): continue if isinstance(type, TypeVarType) and type.values: # Allow substituting T1 for T if every allowed value of T1 # is also a legal value of T. if all( any(is_same_type(v, v1) for v in values) for v1 in type.values): continue for value in values: if isinstance(type, PartialType) or mypy.subtypes.is_subtype( type, value): types[i] = value break else: msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context) upper_bound = callable.variables[i].upper_bound if (type and not isinstance(type, PartialType) and not mypy.subtypes.is_subtype(type, upper_bound)): msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context) # Create a map from type variable id to target type. id_to_type = {} # type: Dict[TypeVarId, Type] for i, tv in enumerate(tvars): if types[i]: id_to_type[tv.id] = types[i] # Apply arguments to argument types. arg_types = [expand_type(at, id_to_type) for at in callable.arg_types] # The callable may retain some type vars if only some were applied. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] return callable.copy_modified( arg_types=arg_types, ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, )
def get_guesses(self, is_method: bool, base: CallableType, defaults: List[Optional[Type]], callsites: List[Callsite]) -> List[CallableType]: """Compute a list of guesses for a function's type. This focuses just on the argument types, and doesn't change the provided return type. """ options = self.get_args(is_method, base, defaults, callsites) return [base.copy_modified(arg_types=list(x)) for x in itertools.product(*options)]
def visit_callable_type(self, t: CallableType) -> Type: return t.copy_modified(arg_types=self.anal_array(t.arg_types, nested=False), ret_type=self.anal_type(t.ret_type, nested=False), fallback=t.fallback or self.builtin_type('builtins.function'), variables=self.anal_var_defs(t.variables))
def refine_callable(t: CallableType, s: CallableType) -> CallableType: """Refine a callable based on another. See comments for refine_type. """ if t.fallback != s.fallback: return t if t.is_ellipsis_args and not is_tricky_callable(s): return s.copy_modified(ret_type=refine_type(t.ret_type, s.ret_type)) if is_tricky_callable(t) or t.arg_kinds != s.arg_kinds: return t return t.copy_modified( arg_types=[refine_type(ta, sa) for ta, sa in zip(t.arg_types, s.arg_types)], ret_type=refine_type(t.ret_type, s.ret_type), )
def apply_generic_arguments(callable: CallableType, types: List[Type], msg: MessageBuilder, context: Context) -> Type: """Apply generic type arguments to a callable type. For example, applying [int] to 'def [T] (T) -> T' results in 'def (int) -> int'. Note that each type can be None; in this case, it will not be applied. """ tvars = callable.variables if len(tvars) != len(types): msg.incompatible_type_application(len(tvars), len(types), context) return AnyType() # Check that inferred type variable values are compatible with allowed # values and bounds. Also, promote subtype values to allowed values. types = types[:] for i, type in enumerate(types): values = callable.variables[i].values if values and type: if isinstance(type, AnyType): continue if isinstance(type, TypeVarType) and type.values: # Allow substituting T1 for T if every allowed value of T1 # is also a legal value of T. if all(any(is_same_type(v, v1) for v in values) for v1 in type.values): continue for value in values: if mypy.subtypes.is_subtype(type, value): types[i] = value break else: msg.incompatible_typevar_value(callable, i + 1, type, context) upper_bound = callable.variables[i].upper_bound if type and not mypy.subtypes.satisfies_upper_bound(type, upper_bound): msg.incompatible_typevar_value(callable, i + 1, type, context) # Create a map from type variable id to target type. id_to_type = {} # type: Dict[TypeVarId, Type] for i, tv in enumerate(tvars): if types[i]: id_to_type[tv.id] = types[i] # Apply arguments to argument types. arg_types = [expand_type(at, id_to_type) for at in callable.arg_types] # The callable may retain some type vars if only some were applied. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] return callable.copy_modified( arg_types=arg_types, ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, )
def apply_generic_arguments( callable: CallableType, orig_types: Sequence[Optional[Type]], report_incompatible_typevar_value: Callable[[CallableType, Type, str, Context], None], context: Context, skip_unsatisfied: bool = False) -> CallableType: """Apply generic type arguments to a callable type. For example, applying [int] to 'def [T] (T) -> T' results in 'def (int) -> int'. Note that each type can be None; in this case, it will not be applied. If `skip_unsatisfied` is True, then just skip the types that don't satisfy type variable bound or constraints, instead of giving an error. """ tvars = callable.variables assert len(tvars) == len(orig_types) # Check that inferred type variable values are compatible with allowed # values and bounds. Also, promote subtype values to allowed values. types = get_proper_types(orig_types) # Create a map from type variable id to target type. id_to_type: Dict[TypeVarId, Type] = {} for tvar, type in zip(tvars, types): assert not isinstance(type, PartialType), "Internal error: must never apply partial type" if type is None: continue target_type = get_target_type( tvar, type, callable, report_incompatible_typevar_value, context, skip_unsatisfied ) if target_type is not None: id_to_type[tvar.id] = target_type param_spec = callable.param_spec() if param_spec is not None: nt = id_to_type.get(param_spec.id) if nt is not None: nt = get_proper_type(nt) if isinstance(nt, CallableType): callable = callable.expand_param_spec(nt) # Apply arguments to argument types. arg_types = [expand_type(at, id_to_type) for at in callable.arg_types] # The callable may retain some type vars if only some were applied. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] return callable.copy_modified( arg_types=arg_types, ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, )
def visit_callable_type(self, t: CallableType) -> Type: if self.check_recursion(t): return AnyType(TypeOfAny.from_error) arg_types = [tp.accept(self) for tp in t.arg_types] ret_type = t.ret_type.accept(self) variables = t.variables.copy() for v in variables: if v.upper_bound: v.upper_bound = v.upper_bound.accept(self) if v.values: v.values = [val.accept(self) for val in v.values] return t.copy_modified(arg_types=arg_types, ret_type=ret_type, variables=variables)
def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance, special_sig: Optional[str]) -> CallableType: """Create a type object type based on the signature of __init__.""" variables = [] # type: List[TypeVarDef] variables.extend(info.defn.type_vars) variables.extend(init_type.variables) callable_type = init_type.copy_modified( ret_type=fill_typevars(info), fallback=type_type, name=None, variables=variables, special_sig=special_sig) c = callable_type.with_name(info.name()) return c
def _change_decorator_function_type( decorator: CallableType, arg_type: CallableType, ) -> CallableType: """Replaces revealed argument types by mypy with types from decorated.""" return decorator.copy_modified( arg_types=arg_type.arg_types, arg_kinds=arg_type.arg_kinds, arg_names=arg_type.arg_names, variables=arg_type.variables, is_ellipsis_args=arg_type.is_ellipsis_args, )
def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: # Every Callable can bind its own type variables, if they're not in the outer scope with self.tvar_scope_frame(): if self.aliasing: variables = t.variables else: variables = self.bind_function_type_variables(t, t) ret = t.copy_modified(arg_types=self.anal_array(t.arg_types, nested=nested), ret_type=self.anal_type(t.ret_type, nested=nested), fallback=t.fallback or self.named_type('builtins.function'), variables=self.anal_var_defs(variables)) return ret
def apply_generic_arguments(callable: CallableType, types: List[Type], msg: MessageBuilder, context: Context) -> Type: """Apply generic type arguments to a callable type. For example, applying [int] to 'def [T] (T) -> T' results in 'def [-1:int] (int) -> int'. Here '[-1:int]' is an implicit bound type variable. Note that each type can be None; in this case, it will not be applied. """ tvars = callable.variables if len(tvars) != len(types): msg.incompatible_type_application(len(tvars), len(types), context) return AnyType() # Check that inferred type variable values are compatible with allowed # values. Also, promote subtype values to allowed values. types = types[:] for i, type in enumerate(types): values = callable.variables[i].values if values and type: if isinstance(type, AnyType): continue for value in values: if mypy.subtypes.is_subtype(type, value): types[i] = value break else: msg.incompatible_typevar_value(callable, i + 1, type, context) # Create a map from type variable id to target type. id_to_type = {} # type: Dict[int, Type] for i, tv in enumerate(tvars): if types[i]: id_to_type[tv.id] = types[i] # Apply arguments to argument types. arg_types = [expand_type(at, id_to_type) for at in callable.arg_types] bound_vars = [(tv.id, id_to_type[tv.id]) for tv in tvars if tv.id in id_to_type] # The callable may retain some type vars if only some were applied. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] return callable.copy_modified( arg_types=arg_types, ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, bound_vars=callable.bound_vars + bound_vars, )
def get_guesses(self, is_method: bool, base: CallableType, defaults: List[Optional[Type]], callsites: List[Callsite], uses: List[List[Type]]) -> List[CallableType]: """Compute a list of guesses for a function's type. This focuses just on the argument types, and doesn't change the provided return type. """ options = self.get_args(is_method, base, defaults, callsites, uses) options = [self.add_adjustments(tps) for tps in options] # Take the first `max_guesses` guesses. product = itertools.islice(itertools.product(*options), 0, self.max_guesses) return [refine_callable(base, base.copy_modified(arg_types=list(x))) for x in product]
def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance) -> CallableType: """Create a type object type based on the signature of __init__.""" variables = [] # type: List[TypeVarDef] for i, tvar in enumerate(info.defn.type_vars): variables.append(TypeVarDef(tvar.name, i + 1, tvar.values, tvar.upper_bound, tvar.variance)) initvars = init_type.variables variables.extend(initvars) callable_type = init_type.copy_modified( ret_type=self_type(info), fallback=type_type, name=None, variables=variables) c = callable_type.with_name('"{}"'.format(info.name())) return convert_class_tvars_to_func_tvars(c, len(initvars))
def combine_similar_callables(t: CallableType, s: CallableType) -> CallableType: arg_types = [] # type: List[Type] for i in range(len(t.arg_types)): arg_types.append(join_types(t.arg_types[i], s.arg_types[i])) # TODO kinds and argument names # The fallback type can be either 'function' or 'type'. The result should have 'type' as # fallback only if both operands have it as 'type'. if t.fallback.type.fullname() != 'builtins.type': fallback = t.fallback else: fallback = s.fallback return t.copy_modified(arg_types=arg_types, ret_type=join_types(t.ret_type, s.ret_type), fallback=fallback, name=None)
def visit_callable_type(self, t: CallableType) -> Type: param_spec = t.param_spec() if param_spec is not None: repl = get_proper_type(self.variables.get(param_spec.id)) # If a ParamSpec in a callable type is substituted with a # callable type, we can't use normal substitution logic, # since ParamSpec is actually split into two components # *P.args and **P.kwargs in the original type. Instead, we # must expand both of them with all the argument types, # kinds and names in the replacement. The return type in # the replacement is ignored. if isinstance(repl, CallableType): # Substitute *args: P.args, **kwargs: P.kwargs t = t.expand_param_spec(repl) # TODO: Substitute remaining arg types return t.copy_modified( ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None)) return t.copy_modified(arg_types=self.expand_types(t.arg_types), ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None))
def visit_callable_type(self, t: CallableType, nested: bool = True) -> Type: # Every Callable can bind its own type variables, if they're not in the outer scope with self.tvar_scope_frame(): if self.defining_alias: variables = t.variables else: variables = self.bind_function_type_variables(t, t) ret = t.copy_modified(arg_types=self.anal_array(t.arg_types, nested=nested), ret_type=self.anal_type(t.ret_type, nested=nested), # If the fallback isn't filled in yet, # its type will be the falsey FakeInfo fallback=(t.fallback if t.fallback.type else self.named_type('builtins.function')), variables=self.anal_var_defs(variables)) return ret
def meet_similar_callables(t: CallableType, s: CallableType) -> CallableType: from mypy.join import join_types arg_types = [] # type: List[Type] for i in range(len(t.arg_types)): arg_types.append(join_types(t.arg_types[i], s.arg_types[i])) # TODO in combine_similar_callables also applies here (names and kinds) # The fallback type can be either 'function' or 'type'. The result should have 'function' as # fallback only if both operands have it as 'function'. if t.fallback.type.fullname() != 'builtins.function': fallback = t.fallback else: fallback = s.fallback return t.copy_modified(arg_types=arg_types, ret_type=meet_types(t.ret_type, s.ret_type), fallback=fallback, name=None)
def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance, special_sig: Optional[str], is_new: bool) -> CallableType: """Create a type object type based on the signature of __init__.""" variables = [] # type: List[TypeVarDef] variables.extend(info.defn.type_vars) variables.extend(init_type.variables) init_ret_type = get_proper_type(init_type.ret_type) if is_new and isinstance(init_ret_type, (Instance, TupleType)): ret_type = init_type.ret_type # type: Type else: ret_type = fill_typevars(info) callable_type = init_type.copy_modified(ret_type=ret_type, fallback=type_type, name=None, variables=variables, special_sig=special_sig) c = callable_type.with_name(info.name()) return c
def visit_callable_type(self, t: CallableType) -> Type: return t.copy_modified(arg_types=self.translate_types(t.arg_types), ret_type=t.ret_type.accept(self), variables=self.translate_variables(t.variables))
def update_callable_implicit_bounds( t: CallableType, arg_types: List[Tuple[int, Type]]) -> CallableType: # FIX what if there are existing bounds? return t.copy_modified(bound_vars=arg_types)
def visit_callable_type(self, t: CallableType) -> Type: return t.copy_modified(arg_types=self.expand_types(t.arg_types), ret_type=t.ret_type.accept(self))
def visit_callable_type(self, t: CallableType) -> ProperType: return self.copy_common(t, t.copy_modified())
def visit_callable_type(self, t: CallableType) -> Type: return t.copy_modified(arg_types=self.expand_types(t.arg_types), ret_type=t.ret_type.accept(self), type_guard=(t.type_guard.accept(self) if t.type_guard is not None else None))
def apply_generic_arguments(callable: CallableType, orig_types: Sequence[Optional[Type]], msg: MessageBuilder, context: Context, skip_unsatisfied: bool = False) -> CallableType: """Apply generic type arguments to a callable type. For example, applying [int] to 'def [T] (T) -> T' results in 'def (int) -> int'. Note that each type can be None; in this case, it will not be applied. If `skip_unsatisfied` is True, then just skip the types that don't satisfy type variable bound or constraints, instead of giving an error. """ tvars = callable.variables assert len(tvars) == len(orig_types) # Check that inferred type variable values are compatible with allowed # values and bounds. Also, promote subtype values to allowed values. types = list(orig_types) for i, type in enumerate(types): assert not isinstance(type, PartialType), "Internal error: must never apply partial type" values = callable.variables[i].values if type is None: continue if values: if isinstance(type, AnyType): continue if isinstance(type, TypeVarType) and type.values: # Allow substituting T1 for T if every allowed value of T1 # is also a legal value of T. if all(any(is_same_type(v, v1) for v in values) for v1 in type.values): continue matching = [] for value in values: if mypy.subtypes.is_subtype(type, value): matching.append(value) if matching: best = matching[0] # If there are more than one matching value, we select the narrowest for match in matching[1:]: if mypy.subtypes.is_subtype(match, best): best = match types[i] = best else: if skip_unsatisfied: types[i] = None else: msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context) else: upper_bound = callable.variables[i].upper_bound if not mypy.subtypes.is_subtype(type, upper_bound): if skip_unsatisfied: types[i] = None else: msg.incompatible_typevar_value(callable, type, callable.variables[i].name, context) # Create a map from type variable id to target type. id_to_type = {} # type: Dict[TypeVarId, Type] for i, tv in enumerate(tvars): typ = types[i] if typ: id_to_type[tv.id] = typ # Apply arguments to argument types. arg_types = [expand_type(at, id_to_type) for at in callable.arg_types] # The callable may retain some type vars if only some were applied. remaining_tvars = [tv for tv in tvars if tv.id not in id_to_type] return callable.copy_modified( arg_types=arg_types, ret_type=expand_type(callable.ret_type, id_to_type), variables=remaining_tvars, )
def _check_typeclass_signature( typeclass_signature: CallableType, instance_signature: CallableType, ctx: MethodContext, ) -> bool: """ Checks that instance signature is compatible with. We use contravariant on arguments and covariant on return type logic here. What does this mean? Let's say that you have this typeclass signature: .. code:: python class A: ... class B(A): ... class C(B): ... @typeclass def some(instance, arg: B) -> B: ... What instance signatures will be compatible? .. code:: python (instance: ..., arg: B) -> B: ... (instance: ..., arg: A) -> C: ... But, any other cases will raise an error. .. note:: We don't check instance types here at all, we replace it with ``Any``. See special function, where we check instance type. """ simplified_typeclass_signature = typeclass_signature.copy_modified( arg_types=[ AnyType(TypeOfAny.implementation_artifact), *typeclass_signature.arg_types[1:], ], ) simplified_instance_signature = instance_signature.copy_modified( arg_types=[ AnyType(TypeOfAny.implementation_artifact), *instance_signature.arg_types[1:], ], ) signature_check = is_subtype( simplified_instance_signature, simplified_typeclass_signature, ) if not signature_check: ctx.api.fail( _INCOMPATIBLE_INSTANCE_SIGNATURE_MSG.format( instance_signature, typeclass_signature.copy_modified(arg_types=[ instance_signature.arg_types[0], # Better error message *typeclass_signature.arg_types[1:], ]), ), ctx.context, ) return signature_check