def no_variant_matches_arguments(self, overload: Overloaded, context: Context) -> None: if overload.name(): self.fail('No overload variant of {} matches argument types' .format(overload.name()), context) else: self.fail('No overload variant matches argument types', context)
def no_variant_matches_arguments(self, overload: Overloaded, arg_types: List[Type], context: Context) -> None: if overload.name(): self.fail('No overload variant of {} matches argument types {}' .format(overload.name(), arg_types), context) else: self.fail('No overload variant matches argument types {}'.format(arg_types), context)
def visit_overloaded(self, t: Overloaded) -> Type: items = [] # type: List[CallableType] for item in t.items(): new_item = item.accept(self) assert isinstance(new_item, CallableType) items.append(new_item) return Overloaded(items)
def visit_overloaded(self, t: Overloaded) -> Type: items = [] # type: List[CallableType] for item in t.items(): new = item.accept(self) if isinstance(new, CallableType): items.append(new) else: raise RuntimeError('CallableType expected, but got {}'.format(type(new))) return Overloaded(items=items)
def visit_overloaded(self, t: Overloaded) -> Type: # TODO: Implement a better algorithm that covers at least the same cases # as TypeJoinVisitor.visit_overloaded(). s = self.s if isinstance(s, FunctionLike): if s.items() == t.items(): return Overloaded(t.items()) elif is_subtype(s, t): return s elif is_subtype(t, s): return t else: return meet_types(t.fallback, s.fallback) elif isinstance(self.s, Instance) and self.s.type.is_protocol: call = unpack_callback_protocol(self.s) if call: return meet_types(t, call) return meet_types(t.fallback, s)
def infer_against_overloaded(self, overloaded: Overloaded, template: Callable) -> List[Constraint]: # Create constraints by matching an overloaded type against a template. # This is tricky to do in general. We cheat by only matching against # the first overload item, and by only matching the return type. This # seems to work somewhat well, but we should really use a more # reliable technique. item = overloaded.items()[0] return infer_constraints(template.ret_type, item.ret_type, self.direction)
def find_matching_overload_item(overloaded: Overloaded, template: CallableType) -> CallableType: """Disambiguate overload item against a template.""" items = overloaded.items() for item in items: # Return type may be indeterminate in the template, so ignore it when performing a # subtype check. if mypy.subtypes.is_callable_subtype(item, template, ignore_return=True): return item # Fall back to the first item if we can't find a match. This is totally arbitrary -- # maybe we should just bail out at this point. return items[0]
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType) or is_named_instance(right, "builtins.type"): for item in left.items(): if is_subtype(item, right, self.check_type_parameter): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i], self.check_type_parameter): return False return True elif isinstance(right, UnboundType): return True else: return False
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType) or is_named_instance( right, 'builtins.type'): for item in left.items(): if is_subtype(item, right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i], self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return False return True elif isinstance(right, UnboundType): return True elif isinstance(right, TypeType): # All the items must have the same type object status, so # it's sufficient to query only (any) one of them. # This is unsound, we don't check the __init__ signature. return left.is_type_obj() and is_subtype(left.items()[0].ret_type, right.item) else: return False
def visit_overloaded(self, t: Overloaded) -> Type: # This is more complex than most other cases. Here are some # examples that illustrate how this works. # # First let's define a concise notation: # - Cn are callable types (for n in 1, 2, ...) # - Ov(C1, C2, ...) is an overloaded type with items C1, C2, ... # - Callable[[T, ...], S] is written as [T, ...] -> S. # # We want some basic properties to hold (assume Cn are all # unrelated via Any-similarity): # # join(Ov(C1, C2), C1) == C1 # join(Ov(C1, C2), Ov(C1, C2)) == Ov(C1, C2) # join(Ov(C1, C2), Ov(C1, C3)) == C1 # join(Ov(C2, C2), C3) == join of fallback types # # The presence of Any types makes things more interesting. The join is the # most general type we can get with respect to Any: # # join(Ov([int] -> int, [str] -> str), [Any] -> str) == Any -> str # # We could use a simplification step that removes redundancies, but that's not # implemented right now. Consider this example, where we get a redundancy: # # join(Ov([int, Any] -> Any, [str, Any] -> Any), [Any, int] -> Any) == # Ov([Any, int] -> Any, [Any, int] -> Any) # # TODO: Consider more cases of callable subtyping. result = [] # type: List[CallableType] s = self.s if isinstance(s, FunctionLike): # The interesting case where both types are function types. for t_item in t.items(): for s_item in s.items(): if is_similar_callables(t_item, s_item): if is_equivalent(t_item, s_item): result.append(combine_similar_callables(t_item, s_item)) elif is_subtype(t_item, s_item): result.append(s_item) if result: # TODO: Simplify redundancies from the result. if len(result) == 1: return result[0] else: return Overloaded(result) return join_types(t.fallback, s.fallback) elif isinstance(s, Instance) and s.type.is_protocol: call = unpack_callback_protocol(s) if call: return join_types(t, call) return join_types(t.fallback, s)
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if is_named_instance(right, 'builtins.object'): return True elif isinstance(right, Callable) or is_named_instance( right, 'builtins.type'): for item in left.items(): if is_subtype(item, right): return True return False elif isinstance(right, Overloaded): # TODO: this may be too restrictive if len(left.items()) != len(right.items()): return False for i in range(len(left.items())): if not is_subtype(left.items()[i], right.items()[i]): return False return True elif isinstance(right, UnboundType): return True else: return False
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 add_class_tvars( t: ProperType, isuper: Optional[Instance], is_classmethod: bool, original_type: Type, original_vars: Optional[Sequence[TypeVarLikeType]] = None) -> Type: """Instantiate type variables during analyze_class_attribute_access, e.g T and Q in the following: class A(Generic[T]): @classmethod def foo(cls: Type[Q]) -> Tuple[T, Q]: ... class B(A[str]): pass B.foo() Args: t: Declared type of the method (or property) isuper: Current instance mapped to the superclass where method was defined, this is usually done by map_instance_to_supertype() is_classmethod: True if this method is decorated with @classmethod original_type: The value of the type B in the expression B.foo() or the corresponding component in case of a union (this is used to bind the self-types) original_vars: Type variables of the class callable on which the method was accessed Returns: Expanded method type with added type variables (when needed). """ # TODO: verify consistency between Q and T # We add class type variables if the class method is accessed on class object # without applied type arguments, this matches the behavior of __init__(). # For example (continuing the example in docstring): # A # The type of callable is def [T] () -> A[T], _not_ def () -> A[Any] # A[int] # The type of callable is def () -> A[int] # and # A.foo # The type is generic def [T] () -> Tuple[T, A[T]] # A[int].foo # The type is non-generic def () -> Tuple[int, A[int]] # # This behaviour is useful for defining alternative constructors for generic classes. # To achieve such behaviour, we add the class type variables that are still free # (i.e. appear in the return type of the class object on which the method was accessed). if isinstance(t, CallableType): tvars = original_vars if original_vars is not None else [] if is_classmethod: t = freshen_function_type_vars(t) t = bind_self(t, original_type, is_classmethod=True) assert isuper is not None t = cast(CallableType, expand_type_by_instance(t, isuper)) freeze_type_vars(t) return t.copy_modified(variables=list(tvars) + list(t.variables)) elif isinstance(t, Overloaded): return Overloaded([ cast( CallableType, add_class_tvars(item, isuper, is_classmethod, original_type, original_vars=original_vars)) for item in t.items() ]) if isuper is not None: t = cast(ProperType, expand_type_by_instance(t, isuper)) return t
def visit_overloaded(self, left: Overloaded) -> bool: if isinstance(self.right, Overloaded): return is_same_types(left.items(), self.right.items()) else: return False
def visit_overloaded(self, t: Overloaded) -> None: for item in t.items(): item.accept(self) # Fallback can be None for overloaded types that haven't been semantically analyzed. if t.fallback is not None: t.fallback.accept(self)
def visit_overloaded(self, t: Overloaded) -> Type: return t.items()[0].accept(self)
def visit_overloaded(self, t: Overloaded) -> None: for item in t.items(): item.accept(self)
def add_class_tvars(t: ProperType, itype: Instance, isuper: Optional[Instance], is_classmethod: bool, builtin_type: Callable[[str], Instance], original_type: Type) -> Type: """Instantiate type variables during analyze_class_attribute_access, e.g T and Q in the following: class A(Generic[T]): @classmethod def foo(cls: Type[Q]) -> Tuple[T, Q]: ... class B(A[str]): pass B.foo() original_type is the value of the type B in the expression B.foo() or the corresponding component in case if a union (this is used to bind the self-types). """ # TODO: verify consistency between Q and T info = itype.type # type: TypeInfo if is_classmethod: assert isuper is not None t = get_proper_type(expand_type_by_instance(t, isuper)) # We add class type variables if the class method is accessed on class object # without applied type arguments, this matches the behavior of __init__(). # For example (continuing the example in docstring): # A # The type of callable is def [T] () -> A[T], _not_ def () -> A[Any] # A[int] # The type of callable is def () -> A[int] # and # A.foo # The type is generic def [T] () -> Tuple[T, A[T]] # A[int].foo # The type is non-generic def () -> Tuple[int, A[int]] # # This behaviour is useful for defining alternative constructors for generic classes. # To achieve such behaviour, we add the class type variables that are still free # (i.e. appear in the return type of the class object on which the method was accessed). free_ids = {t.id for t in itype.args if isinstance(t, TypeVarType)} if isinstance(t, CallableType): # NOTE: in practice either all or none of the variables are free, since # visit_type_application() will detect any type argument count mismatch and apply # a correct number of Anys. tvars = [ TypeVarDef(n, n, i + 1, [], builtin_type('builtins.object'), tv.variance) for (i, n), tv in zip(enumerate(info.type_vars), info.defn.type_vars) # use 'is' to avoid id clashes with unrelated variables if any(tv.id is id for id in free_ids) ] if is_classmethod: t = bind_self(t, original_type, is_classmethod=True) return t.copy_modified(variables=tvars + t.variables) elif isinstance(t, Overloaded): return Overloaded([ cast( CallableType, add_class_tvars(item, itype, isuper, is_classmethod, builtin_type, original_type)) for item in t.items() ]) return t
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType): for item in left.items(): if is_subtype(item, right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return True return False elif isinstance(right, Overloaded): # Ensure each overload in the right side (the supertype) is accounted for. previous_match_left_index = -1 matched_overloads = set() possible_invalid_overloads = set() for right_index, right_item in enumerate(right.items()): found_match = False for left_index, left_item in enumerate(left.items()): subtype_match = is_subtype( left_item, right_item, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names) # Order matters: we need to make sure that the index of # this item is at least the index of the previous one. if subtype_match and previous_match_left_index <= left_index: if not found_match: # Update the index of the previous match. previous_match_left_index = left_index found_match = True matched_overloads.add(left_item) possible_invalid_overloads.discard(left_item) else: # If this one overlaps with the supertype in any way, but it wasn't # an exact match, then it's a potential error. if (is_callable_compatible( left_item, right_item, is_compat=is_subtype, ignore_return=True, ignore_pos_arg_names=self.ignore_pos_arg_names) or is_callable_compatible( right_item, left_item, is_compat=is_subtype, ignore_return=True, ignore_pos_arg_names=self. ignore_pos_arg_names)): # If this is an overload that's already been matched, there's no # problem. if left_item not in matched_overloads: possible_invalid_overloads.add(left_item) if not found_match: return False if possible_invalid_overloads: # There were potentially invalid overloads that were never matched to the # supertype. return False return True elif isinstance(right, UnboundType): return True elif isinstance(right, TypeType): # All the items must have the same type object status, so # it's sufficient to query only (any) one of them. # This is unsound, we don't check all the __init__ signatures. return left.is_type_obj() and is_subtype(left.items()[0], right) else: return False
def visit_overloaded(self, typ: Overloaded) -> SnapshotItem: return ('Overloaded', snapshot_types(typ.items()))
def visit_overloaded(self, t: types.Overloaded) -> Set[str]: return self._visit(t.items()) | self._visit(t.fallback)
def takes_callable_and_args_callback(ctx: FunctionContext) -> Type: """Automate the boilerplate for writing functions that accept arbitrary positional arguments of the same type accepted by a callable. For example, this supports writing:: @trio_typing.takes_callable_and_args def start_soon( self, async_fn: Callable[[trio_typing.ArgsForCallable], None], *args: trio_typing.ArgsForCallable, ) -> None: ... instead of:: T1 = TypeVar("T1") T2 = TypeVar("T2") T3 = TypeVar("T3") T4 = TypeVar("T4") @overload def start_soon( self, async_fn: Callable[[], None], ) -> None: ... @overload def start_soon( self, async_fn: Callable[[T1], None], __arg1: T1, ) -> None: ... @overload def start_soon( self, async_fn: Callable[[T1, T2], None], __arg1: T1, __arg2: T2 ) -> None: ... # etc """ try: if (not ctx.arg_types or len(ctx.arg_types[0]) != 1 or not isinstance(ctx.arg_types[0][0], CallableType) or not isinstance(ctx.default_return_type, CallableType)): raise ValueError("must be used as a decorator") fn_type = ctx.arg_types[0][0] # type: CallableType callable_idx = -1 # index in function arguments of the callable callable_args_idx = -1 # index in callable arguments of the StarArgs args_idx = -1 # index in function arguments of the StarArgs for idx, (kind, ty) in enumerate(zip(fn_type.arg_kinds, fn_type.arg_types)): if (isinstance(ty, Instance) and ty.type.fullname() == "trio_typing.ArgsForCallable"): if kind != ARG_STAR: raise ValueError( "ArgsForCallable must be used with a *args argument " "in the decorated function") assert args_idx == -1 args_idx = idx elif isinstance(ty, CallableType) and kind == ARG_POS: for idx_, (kind_, ty_) in enumerate(zip(ty.arg_kinds, ty.arg_types)): if (isinstance(ty_, Instance) and ty_.type.fullname() == "trio_typing.ArgsForCallable"): if kind != ARG_POS: raise ValueError( "ArgsForCallable must be used with a positional " "argument in the callable type that the decorated " "function takes") if callable_idx != -1: raise ValueError( "ArgsForCallable may only be used once as the type " "of an argument to a callable type that the " "decorated function takes") callable_idx = idx callable_args_idx = idx_ if args_idx == -1: raise ValueError("decorated function must take *args with type " "trio_typing.ArgsForCallable") if callable_idx == -1: raise ValueError( "decorated function must take a callable that has an " "argument of type trio_typing.ArgsForCallable") expanded_fns = [] # type: List[CallableType] type_var_defs = [] # type: List[TypeVarDef] type_var_types = [] # type: List[Type] for arg_idx in range(1, 5): callable_ty = cast(CallableType, fn_type.arg_types[callable_idx]) arg_types = list(fn_type.arg_types) arg_types[callable_idx] = callable_ty.copy_modified( arg_types=(callable_ty.arg_types[:callable_args_idx] + type_var_types + callable_ty.arg_types[callable_args_idx + 1:]), arg_kinds=(callable_ty.arg_kinds[:callable_args_idx] + ([ARG_POS] * len(type_var_types)) + callable_ty.arg_kinds[callable_args_idx + 1:]), arg_names=(callable_ty.arg_names[:callable_args_idx] + ([None] * len(type_var_types)) + callable_ty.arg_names[callable_args_idx + 1:]), variables=(callable_ty.variables + type_var_defs), ) expanded_fns.append( fn_type.copy_modified( arg_types=(arg_types[:args_idx] + type_var_types + arg_types[args_idx + 1:]), arg_kinds=(fn_type.arg_kinds[:args_idx] + ([ARG_POS] * len(type_var_types)) + fn_type.arg_kinds[args_idx + 1:]), arg_names=(fn_type.arg_names[:args_idx] + ([None] * len(type_var_types)) + fn_type.arg_names[args_idx + 1:]), variables=(fn_type.variables + type_var_defs), )) type_var_defs.append( TypeVarDef( "__T{}".format(arg_idx), "__T{}".format(arg_idx), -len(fn_type.variables) - arg_idx - 1, [], ctx.api.named_generic_type("builtins.object", []), )) type_var_types.append( TypeVarType(type_var_defs[-1], ctx.context.line, ctx.context.column)) return Overloaded(expanded_fns) except ValueError as ex: ctx.api.fail("invalid use of @takes_callable_and_args: {}".format(ex), ctx.context) return ctx.default_return_type
def visit_overloaded(self, t: Overloaded) -> None: self.traverse_types(t.items())
def visit_overloaded(self, t: Overloaded) -> Type: items = [] # type: List[CallableType] for item in t.items(): items.append(cast(CallableType, item.accept(self))) return Overloaded(items)
def visit_overloaded(self, t: types.Overloaded) -> Set[str]: return self._visit(*t.items()) | self._visit(t.fallback)
def visit_overloaded(self, t: Overloaded) -> T: return self.query_types(t.items())
def takes_callable_and_args_callback(ctx: FunctionContext) -> Type: """Automate the boilerplate for writing functions that accept arbitrary positional arguments of the same type accepted by a callable. For example, this supports writing:: @trio_typing.takes_callable_and_args def start_soon( self, async_fn: Callable[[VarArg()], Any], *args: Any, ) -> None: ... instead of:: T1 = TypeVar("T1") T2 = TypeVar("T2") T3 = TypeVar("T3") T4 = TypeVar("T4") @overload def start_soon( self, async_fn: Callable[[], Any], ) -> None: ... @overload def start_soon( self, async_fn: Callable[[T1], Any], __arg1: T1, ) -> None: ... @overload def start_soon( self, async_fn: Callable[[T1, T2], Any], __arg1: T1, __arg2: T2 ) -> None: ... # etc """ try: if not ctx.arg_types or len(ctx.arg_types[0]) != 1: raise ValueError("must be used as a decorator") fn_type = get_proper_type(ctx.arg_types[0][0]) if not isinstance(fn_type, CallableType) or not isinstance( get_proper_type(ctx.default_return_type), CallableType): raise ValueError("must be used as a decorator") callable_idx = -1 # index in function arguments of the callable callable_args_idx = -1 # index in callable arguments of the StarArgs callable_ty = None # type: Optional[CallableType] args_idx = -1 # index in function arguments of the StarArgs for idx, (kind, ty) in enumerate(zip(fn_type.arg_kinds, fn_type.arg_types)): ty = get_proper_type(ty) if isinstance(ty, AnyType) and kind == ARG_STAR: assert args_idx == -1 args_idx = idx elif isinstance(ty, (UnionType, CallableType)) and kind == ARG_POS: # turn Union[Callable[..., T], Callable[[VarArg()], T]] # into Callable[[VarArg()], T] # (the union makes it not fail when the plugin is not being used) if isinstance(ty, UnionType): for arm in get_proper_types(ty.items): if (isinstance(arm, CallableType) and not arm.is_ellipsis_args and any(kind_ == ARG_STAR for kind_ in arm.arg_kinds)): ty = arm break else: continue for idx_, (kind_, ty_) in enumerate(zip(ty.arg_kinds, ty.arg_types)): ty_ = get_proper_type(ty_) if isinstance(ty_, AnyType) and kind_ == ARG_STAR: if callable_idx != -1: raise ValueError( "decorated function may only take one callable " "that has an argument of type VarArg()") callable_idx = idx callable_args_idx = idx_ callable_ty = ty if args_idx == -1: raise ValueError("decorated function must take *args: Any") if callable_idx == -1 or callable_ty is None: raise ValueError( "decorated function must take a callable that has a " "argument of type mypy_extensions.VarArg()") expanded_fns = [] # type: List[CallableType] type_var_defs = [] # type: List[TypeVarDef] type_var_types = [] # type: List[Type] for arg_idx in range( 1, 7): # provides overloads for 0 through 5 arguments arg_types = list(fn_type.arg_types) arg_types[callable_idx] = callable_ty.copy_modified( arg_types=(callable_ty.arg_types[:callable_args_idx] + type_var_types + callable_ty.arg_types[callable_args_idx + 1:]), arg_kinds=(callable_ty.arg_kinds[:callable_args_idx] + ([ARG_POS] * len(type_var_types)) + callable_ty.arg_kinds[callable_args_idx + 1:]), arg_names=(callable_ty.arg_names[:callable_args_idx] + ([None] * len(type_var_types)) + callable_ty.arg_names[callable_args_idx + 1:]), variables=(callable_ty.variables + type_var_defs), ) expanded_fns.append( fn_type.copy_modified( arg_types=(arg_types[:args_idx] + type_var_types + arg_types[args_idx + 1:]), arg_kinds=(fn_type.arg_kinds[:args_idx] + ([ARG_POS] * len(type_var_types)) + fn_type.arg_kinds[args_idx + 1:]), arg_names=(fn_type.arg_names[:args_idx] + ([None] * len(type_var_types)) + fn_type.arg_names[args_idx + 1:]), variables=(fn_type.variables + type_var_defs), )) type_var_defs.append( TypeVarDef( "__T{}".format(arg_idx), "__T{}".format(arg_idx), -len(fn_type.variables) - arg_idx - 1, [], ctx.api.named_generic_type("builtins.object", []), )) type_var_types.append( TypeVarType(type_var_defs[-1], ctx.context.line, ctx.context.column)) return Overloaded(expanded_fns) except ValueError as ex: ctx.api.fail("invalid use of @takes_callable_and_args: {}".format(ex), ctx.context) return ctx.default_return_type
def ov(*items: CallableType) -> Overloaded: return Overloaded(list(items))
def translate_kind_instance(typ: Type) -> Type: # noqa: WPS, C901 """ We use this ugly hack to translate ``KindN[x, y]`` into ``x[y]``. This is required due to the fact that ``KindN`` can be nested in other types, like: ``List[KindN[...]]``. We will refactor this code after ``TypeTranslator`` is released in ``[email protected]`` version. """ typ = get_proper_type(typ) if isinstance(typ, _LEAF_TYPES): # noqa: WPS223 return typ elif isinstance(typ, Instance): last_known_value: Optional[LiteralType] = None if typ.last_known_value is not None: raw_last_known_value = translate_kind_instance( typ.last_known_value) assert isinstance(raw_last_known_value, LiteralType) last_known_value = raw_last_known_value instance = Instance( typ=typ.type, args=_translate_types(typ.args), line=typ.line, column=typ.column, last_known_value=last_known_value, ) if typ.type.fullname == TYPED_KINDN: # That's where we do the change return _process_kinded_type(instance) return instance elif isinstance(typ, CallableType): return typ.copy_modified( arg_types=_translate_types(typ.arg_types), ret_type=translate_kind_instance(typ.ret_type), ) elif isinstance(typ, TupleType): return TupleType( _translate_types(typ.items), translate_kind_instance(typ.partial_fallback), # type: ignore typ.line, typ.column, ) elif isinstance(typ, TypedDictType): dict_items = { item_name: translate_kind_instance(item_type) for item_name, item_type in typ.items.items() } return TypedDictType( dict_items, typ.required_keys, translate_kind_instance(typ.fallback), # type: ignore typ.line, typ.column, ) elif isinstance(typ, LiteralType): fallback = translate_kind_instance(typ.fallback) assert isinstance(fallback, Instance) return LiteralType( value=typ.value, fallback=fallback, line=typ.line, column=typ.column, ) elif isinstance(typ, UnionType): return UnionType(_translate_types(typ.items), typ.line, typ.column) elif isinstance(typ, Overloaded): functions: List[CallableType] = [] for func in typ.items(): new = translate_kind_instance(func) assert isinstance(new, CallableType) functions.append(new) return Overloaded(items=functions) elif isinstance(typ, TypeType): return TypeType.make_normalized( translate_kind_instance(typ.item), line=typ.line, column=typ.column, ) return typ
def visit_overloaded(self, typ: Overloaded) -> List[str]: triggers = [] for item in typ.items(): triggers.extend(self.get_type_triggers(item)) return triggers
def visit_overloaded(self, left: Overloaded) -> bool: right = self.right if isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, CallableType): for item in left.items(): if is_subtype(item, right, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names): return True return False elif isinstance(right, Overloaded): # Ensure each overload in the right side (the supertype) is accounted for. previous_match_left_index = -1 matched_overloads = set() possible_invalid_overloads = set() for right_index, right_item in enumerate(right.items()): found_match = False for left_index, left_item in enumerate(left.items()): subtype_match = is_subtype(left_item, right_item, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names) # Order matters: we need to make sure that the index of # this item is at least the index of the previous one. if subtype_match and previous_match_left_index <= left_index: if not found_match: # Update the index of the previous match. previous_match_left_index = left_index found_match = True matched_overloads.add(left_item) possible_invalid_overloads.discard(left_item) else: # If this one overlaps with the supertype in any way, but it wasn't # an exact match, then it's a potential error. if (is_callable_subtype(left_item, right_item, ignore_return=True, ignore_pos_arg_names=self.ignore_pos_arg_names) or is_callable_subtype(right_item, left_item, ignore_return=True, ignore_pos_arg_names=self.ignore_pos_arg_names)): # If this is an overload that's already been matched, there's no # problem. if left_item not in matched_overloads: possible_invalid_overloads.add(left_item) if not found_match: return False if possible_invalid_overloads: # There were potentially invalid overloads that were never matched to the # supertype. return False return True elif isinstance(right, UnboundType): return True elif isinstance(right, TypeType): # All the items must have the same type object status, so # it's sufficient to query only (any) one of them. # This is unsound, we don't check all the __init__ signatures. return left.is_type_obj() and is_subtype(left.items()[0], right) else: return False
def _curry_hook(context: FunctionContext) -> Type: arg_type = context.arg_types[0][0] function = _get_callable_type(arg_type, context) if function is None: # argument was not callable type or function return context.default_return_type if len(function.arg_types) < 2: # nullary or unary function: nothing to do return function type_vars = {var.fullname: var for var in function.variables} def get_variables(*types): def collect_variables(*ts): variables = [] for type_ in ts: if isinstance(type_, TypeVarType): variables.append(type_) if hasattr(type_, 'args'): variables += get_variables(*type_.args) if isinstance(type_, CallableType): variables += get_variables(type_.ret_type, *type_.arg_types) if isinstance(type_, UnionType): variables += get_variables(*type_.items) return variables return set(type_vars[v.fullname] for v in collect_variables(*types)) args = tuple( zip(function.arg_types, function.arg_kinds, function.arg_names)) optional_args = tuple( filter(lambda a: a[1] in (ARG_OPT, ARG_NAMED_OPT, ARG_STAR2), args)) positional_args = tuple( filter(lambda a: a[1] in (ARG_POS, ARG_NAMED, ARG_STAR), args)) if not positional_args: # no positional args: nothing to do return function opt_arg_types, opt_arg_kinds, opt_arg_names = (tuple(zip( *optional_args)) if optional_args else ((), (), ())) pos_arg_types, pos_arg_kinds, pos_arg_names = tuple(zip(*positional_args)) arg_type, *arg_types = pos_arg_types arg_name, *arg_names = pos_arg_names arg_kind, *arg_kinds = pos_arg_kinds return_type = function.ret_type opt_variables = get_variables(*opt_arg_types) variables = get_variables(arg_type) if len(pos_arg_types) == 1: variables |= get_variables(return_type) ret_type = return_type else: ret_type = AnyType(TypeOfAny.special_form) functions = [ CallableType(arg_types=[arg_type], arg_kinds=[arg_kind], arg_names=[arg_name], ret_type=ret_type, fallback=function.fallback, variables=list(variables)) ] remaining_args = zip(arg_types, arg_kinds, arg_names) for i, (arg_type, kind, name) in enumerate(remaining_args): variables = get_variables(arg_type) if i == len(arg_types) - 1: variables |= get_variables(return_type) ret_type = return_type else: ret_type = AnyType(TypeOfAny.special_form) variables -= set.union(*[set(f.variables) for f in functions]) if kind == ARG_STAR: last_f = functions[i] functions[i] = last_f.copy_modified( arg_types=last_f.arg_types + [arg_type], arg_kinds=last_f.arg_kinds + [kind], arg_names=last_f.arg_names + [name], variables=list(sorted(variables, key=str)), ret_type=ret_type) else: functions.append( CallableType(arg_types=[arg_type], arg_kinds=[kind], arg_names=[name], ret_type=ret_type, fallback=function.fallback, variables=list(sorted(variables, key=str)))) def merge(fs): if len(fs) == 1: return fs[0] first, next_, *rest = fs first.ret_type = next_ for f in rest: next_.ret_type = f next_ = f return first merged = merge(functions) if optional_args: mod_functions = [ f.copy_modified(variables=list( sorted(set(f.variables) - opt_variables, key=str))) for f in functions ] mod_merged = merge(mod_functions) with_opts = CallableType(arg_types=list(opt_arg_types), arg_kinds=list(opt_arg_kinds), arg_names=list(opt_arg_names), ret_type=mod_merged, fallback=function.fallback, variables=list(sorted(opt_variables, key=str))) return Overloaded([merged, function, with_opts]) return Overloaded([merged, function])
def visit_overloaded(self, template: Overloaded) -> List[Constraint]: res = [] # type: List[Constraint] for t in template.items(): res.extend(infer_constraints(t, self.actual, self.direction)) return res
def ov(*items): return Overloaded(items)
def visit_overloaded(self, t: Overloaded) -> ProperType: return self.copy_common(t, Overloaded(items=t.items))
def visit_overloaded(self, t: Overloaded) -> Type: items = [] # type: List[Callable] for item in t.items(): items.append(cast(Callable, item.accept(self))) return Overloaded(items)
def visit_overloaded(self, t: Overloaded) -> None: for ct in t.items(): ct.accept(self)
def visit_overloaded(self, typ: Overloaded) -> List[str]: triggers = [] for item in typ.items(): triggers.extend(get_type_triggers(item)) return triggers
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 = [] # type: Sequence[TypeVarLikeDef] 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)