def callable_corresponding_argument(typ: CallableType, model: FormalArgument) -> Optional[FormalArgument]: """Return the argument a function that corresponds to `model`""" by_name = typ.argument_by_name(model.name) by_pos = typ.argument_by_position(model.pos) if by_name is None and by_pos is None: return None if by_name is not None and by_pos is not None: if by_name == by_pos: return by_name # If we're dealing with an optional pos-only and an optional # name-only arg, merge them. This is the case for all functions # taking both *args and **args, or a pair of functions like so: # def right(a: int = ...) -> None: ... # def left(__a: int = ..., *, a: int = ...) -> None: ... from mypy.subtypes import is_equivalent if (not (by_name.required or by_pos.required) and by_pos.name is None and by_name.pos is None and is_equivalent(by_name.typ, by_pos.typ)): return FormalArgument(by_name.name, by_pos.pos, by_name.typ, False) return by_name if by_name is not None else by_pos
def is_callable_subtype(left: CallableType, right: CallableType, ignore_return: bool = False, ignore_pos_arg_names: bool = False) -> bool: """Is left a subtype of right?""" # If either function is implicitly typed, ignore positional arg names too if left.implicit or right.implicit: ignore_pos_arg_names = True # Non-type cannot be a subtype of type. if right.is_type_obj() and not left.is_type_obj(): return False # A callable L is a subtype of a generic callable R if L is a # subtype of every type obtained from R by substituting types for # the variables of R. We can check this by simply leaving the # generic variables of R as type variables, effectively varying # over all possible values. # It's okay even if these variables share ids with generic # type variables of L, because generating and solving # constraints for the variables of L to make L a subtype of R # (below) treats type variables on the two sides as independent. if left.variables: # Apply generic type variables away in left via type inference. left = unify_generic_callable(left, right, ignore_return=ignore_return) if left is None: return False # Check return types. if not ignore_return and not is_subtype(left.ret_type, right.ret_type): return False if right.is_ellipsis_args: return True right_star_type = None # type: Optional[Type] right_star2_type = None # type: Optional[Type] # Match up corresponding arguments and check them for compatibility. In # every pair (argL, argR) of corresponding arguments from L and R, argL must # be "more general" than argR if L is to be a subtype of R. # Arguments are corresponding if they either share a name, share a position, # or both. If L's corresponding argument is ambiguous, L is not a subtype of # R. # If left has one corresponding argument by name and another by position, # consider them to be one "merged" argument (and not ambiguous) if they're # both optional, they're name-only and position-only respectively, and they # have the same type. This rule allows functions with (*args, **kwargs) to # properly stand in for the full domain of formal arguments that they're # used for in practice. # Every argument in R must have a corresponding argument in L, and every # required argument in L must have a corresponding argument in R. done_with_positional = False for i in range(len(right.arg_types)): right_kind = right.arg_kinds[i] if right_kind in (ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT): done_with_positional = True right_required = right_kind in (ARG_POS, ARG_NAMED) right_pos = None if done_with_positional else i right_arg = FormalArgument( right.arg_names[i], right_pos, right.arg_types[i], right_required) if right_kind == ARG_STAR: right_star_type = right_arg.typ # Right has an infinite series of optional positional arguments # here. Get all further positional arguments of left, and make sure # they're more general than their corresponding member in this # series. Also make sure left has its own inifite series of # optional positional arguments. if not left.is_var_arg: return False j = i while j < len(left.arg_kinds) and left.arg_kinds[j] in (ARG_POS, ARG_OPT): left_by_position = left.argument_by_position(j) assert left_by_position is not None # This fetches the synthetic argument that's from the *args right_by_position = right.argument_by_position(j) assert right_by_position is not None if not are_args_compatible(left_by_position, right_by_position, ignore_pos_arg_names): return False j += 1 continue if right_kind == ARG_STAR2: right_star2_type = right_arg.typ # Right has an infinite set of optional named arguments here. Get # all further named arguments of left and make sure they're more # general than their corresponding member in this set. Also make # sure left has its own infinite set of optional named arguments. if not left.is_kw_arg: return False left_names = {name for name in left.arg_names if name is not None} right_names = {name for name in right.arg_names if name is not None} left_only_names = left_names - right_names for name in left_only_names: left_by_name = left.argument_by_name(name) assert left_by_name is not None # This fetches the synthetic argument that's from the **kwargs right_by_name = right.argument_by_name(name) assert right_by_name is not None if not are_args_compatible(left_by_name, right_by_name, ignore_pos_arg_names): return False continue # Left must have some kind of corresponding argument. left_arg = left.corresponding_argument(right_arg) if left_arg is None: return False if not are_args_compatible(left_arg, right_arg, ignore_pos_arg_names): return False done_with_positional = False for i in range(len(left.arg_types)): left_kind = left.arg_kinds[i] if left_kind in (ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT): done_with_positional = True left_arg = FormalArgument( left.arg_names[i], None if done_with_positional else i, left.arg_types[i], left_kind in (ARG_POS, ARG_NAMED)) # Check that *args and **kwargs types match in this loop if left_kind == ARG_STAR: if right_star_type is not None and not is_subtype(right_star_type, left_arg.typ): return False continue elif left_kind == ARG_STAR2: if right_star2_type is not None and not is_subtype(right_star2_type, left_arg.typ): return False continue right_by_name = (right.argument_by_name(left_arg.name) if left_arg.name is not None else None) right_by_pos = (right.argument_by_position(left_arg.pos) if left_arg.pos is not None else None) # If the left hand argument corresponds to two right-hand arguments, # neither of them can be required. if (right_by_name is not None and right_by_pos is not None and right_by_name != right_by_pos and (right_by_pos.required or right_by_name.required)): return False # All *required* left-hand arguments must have a corresponding # right-hand argument. Optional args it does not matter. if left_arg.required and right_by_pos is None and right_by_name is None: return False return True
def is_callable_compatible(left: CallableType, right: CallableType, *, is_compat: Callable[[Type, Type], bool], is_compat_return: Optional[Callable[[Type, Type], bool]] = None, ignore_return: bool = False, ignore_pos_arg_names: bool = False, check_args_covariantly: bool = False) -> bool: """Is the left compatible with the right, using the provided compatibility check? is_compat: The check we want to run against the parameters. is_compat_return: The check we want to run against the return type. If None, use the 'is_compat' check. check_args_covariantly: If true, check if the left's args is compatible with the right's instead of the other way around (contravariantly). This function is mostly used to check if the left is a subtype of the right which is why the default is to check the args contravariantly. However, it's occasionally useful to check the args using some other check, so we leave the variance configurable. For example, when checking the validity of overloads, it's useful to see if the first overload alternative has more precise arguments then the second. We would want to check the arguments covariantly in that case. Note! The following two function calls are NOT equivalent: is_callable_compatible(f, g, is_compat=is_subtype, check_args_covariantly=False) is_callable_compatible(g, f, is_compat=is_subtype, check_args_covariantly=True) The two calls are similar in that they both check the function arguments in the same direction: they both run `is_subtype(argument_from_g, argument_from_f)`. However, the two calls differ in which direction they check things likee keyword arguments. For example, suppose f and g are defined like so: def f(x: int, *y: int) -> int: ... def g(x: int) -> int: ... In this case, the first call will succeed and the second will fail: f is a valid stand-in for g but not vice-versa. """ if is_compat_return is None: is_compat_return = is_compat # If either function is implicitly typed, ignore positional arg names too if left.implicit or right.implicit: ignore_pos_arg_names = True # Non-type cannot be a subtype of type. if right.is_type_obj() and not left.is_type_obj(): return False # A callable L is a subtype of a generic callable R if L is a # subtype of every type obtained from R by substituting types for # the variables of R. We can check this by simply leaving the # generic variables of R as type variables, effectively varying # over all possible values. # It's okay even if these variables share ids with generic # type variables of L, because generating and solving # constraints for the variables of L to make L a subtype of R # (below) treats type variables on the two sides as independent. if left.variables: # Apply generic type variables away in left via type inference. unified = unify_generic_callable(left, right, ignore_return=ignore_return) if unified is None: return False else: left = unified # Check return types. if not ignore_return and not is_compat_return(left.ret_type, right.ret_type): return False if check_args_covariantly: is_compat = flip_compat_check(is_compat) if right.is_ellipsis_args: return True right_star_type = None # type: Optional[Type] right_star2_type = None # type: Optional[Type] # Match up corresponding arguments and check them for compatibility. In # every pair (argL, argR) of corresponding arguments from L and R, argL must # be "more general" than argR if L is to be a subtype of R. # Arguments are corresponding if they either share a name, share a position, # or both. If L's corresponding argument is ambiguous, L is not a subtype of # R. # If left has one corresponding argument by name and another by position, # consider them to be one "merged" argument (and not ambiguous) if they're # both optional, they're name-only and position-only respectively, and they # have the same type. This rule allows functions with (*args, **kwargs) to # properly stand in for the full domain of formal arguments that they're # used for in practice. # Every argument in R must have a corresponding argument in L, and every # required argument in L must have a corresponding argument in R. done_with_positional = False for i in range(len(right.arg_types)): right_kind = right.arg_kinds[i] if right_kind in (ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT): done_with_positional = True right_required = right_kind in (ARG_POS, ARG_NAMED) right_pos = None if done_with_positional else i right_arg = FormalArgument(right.arg_names[i], right_pos, right.arg_types[i], right_required) if right_kind == ARG_STAR: right_star_type = right_arg.typ # Right has an infinite series of optional positional arguments # here. Get all further positional arguments of left, and make sure # they're more general than their corresponding member in this # series. Also make sure left has its own infinite series of # optional positional arguments. if not left.is_var_arg: return False j = i while j < len(left.arg_kinds) and left.arg_kinds[j] in (ARG_POS, ARG_OPT): left_by_position = left.argument_by_position(j) assert left_by_position is not None # This fetches the synthetic argument that's from the *args right_by_position = right.argument_by_position(j) assert right_by_position is not None if not are_args_compatible(left_by_position, right_by_position, ignore_pos_arg_names, is_compat): return False j += 1 continue if right_kind == ARG_STAR2: right_star2_type = right_arg.typ # Right has an infinite set of optional named arguments here. Get # all further named arguments of left and make sure they're more # general than their corresponding member in this set. Also make # sure left has its own infinite set of optional named arguments. if not left.is_kw_arg: return False left_names = {name for name in left.arg_names if name is not None} right_names = { name for name in right.arg_names if name is not None } left_only_names = left_names - right_names for name in left_only_names: left_by_name = left.argument_by_name(name) assert left_by_name is not None # This fetches the synthetic argument that's from the **kwargs right_by_name = right.argument_by_name(name) assert right_by_name is not None if not are_args_compatible(left_by_name, right_by_name, ignore_pos_arg_names, is_compat): return False continue # Left must have some kind of corresponding argument. left_arg = left.corresponding_argument(right_arg) if left_arg is None: return False if not are_args_compatible(left_arg, right_arg, ignore_pos_arg_names, is_compat): return False done_with_positional = False for i in range(len(left.arg_types)): left_kind = left.arg_kinds[i] if left_kind in (ARG_STAR, ARG_STAR2, ARG_NAMED, ARG_NAMED_OPT): done_with_positional = True left_arg = FormalArgument(left.arg_names[i], None if done_with_positional else i, left.arg_types[i], left_kind in (ARG_POS, ARG_NAMED)) # Check that *args and **kwargs types match in this loop if left_kind == ARG_STAR: if right_star_type is not None and not is_compat( right_star_type, left_arg.typ): return False continue elif left_kind == ARG_STAR2: if right_star2_type is not None and not is_compat( right_star2_type, left_arg.typ): return False continue right_by_name = (right.argument_by_name(left_arg.name) if left_arg.name is not None else None) right_by_pos = (right.argument_by_position(left_arg.pos) if left_arg.pos is not None else None) # If the left hand argument corresponds to two right-hand arguments, # neither of them can be required. if (right_by_name is not None and right_by_pos is not None and right_by_name != right_by_pos and (right_by_pos.required or right_by_name.required)): return False # All *required* left-hand arguments must have a corresponding # right-hand argument. Optional args it does not matter. if left_arg.required and right_by_pos is None and right_by_name is None: return False return True