def is_callable_subtype(left: CallableType, right: CallableType, ignore_return: bool = False) -> bool: """Is left a subtype of right?""" # TODO: Support named arguments, **args, etc. # Non-type cannot be a subtype of type. if right.is_type_obj() and not left.is_type_obj(): return False if right.variables: # Subtyping is not currently supported for generic function as the supertype. return False if left.variables: # Apply generic type variables away in left via type inference. left = unify_generic_callable(left, right) 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 # Check argument types. if left.min_args > right.min_args: return False if left.is_var_arg: return is_var_arg_callable_subtype_helper(left, right) if right.is_var_arg: return False if len(left.arg_types) < len(right.arg_types): return False for i in range(len(right.arg_types)): if not is_subtype(right.arg_types[i], left.arg_types[i]): return False return True
def add_method(self, method_name: str, args: List[Argument], ret_type: Type, self_type: Optional[Type] = None, tvd: Optional[TypeVarDef] = None) -> None: """Add a method: def <method_name>(self, <args>) -> <ret_type>): ... to info. self_type: The type to use for the self argument or None to use the inferred self type. tvd: If the method is generic these should be the type variables. """ from mypy.semanal import set_callable_name self_type = self_type if self_type is not None else self.self_type args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types = [arg.type_annotation for arg in args] arg_names = [arg.variable.name() for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in arg_types signature = CallableType(cast(List[Type], arg_types), arg_kinds, arg_names, ret_type, self.function_type) if tvd: signature.variables = [tvd] func = FuncDef(method_name, args, Block([PassStmt()])) func.info = self.info func.type = set_callable_name(signature, func) func._fullname = self.info.fullname() + '.' + method_name func.line = self.info.line self.info.names[method_name] = SymbolTableNode(MDEF, func) # Add the created methods to the body so that they can get further semantic analysis. # e.g. Forward Reference Resolution. self.info.defn.defs.body.append(func)
def add_method( ctx: ClassDefContext, name: str, args: List[Argument], return_type: Type, self_type: Optional[Type] = None, tvar_def: Optional[TypeVarDef] = None, ) -> None: """Adds a new method to a class. """ info = ctx.cls.info self_type = self_type or fill_typevars(info) function_type = ctx.api.named_type('__builtins__.function') args = [Argument(Var('self'), self_type, None, ARG_POS)] + args arg_types, arg_names, arg_kinds = [], [], [] for arg in args: assert arg.type_annotation, 'All arguments must be fully typed.' arg_types.append(arg.type_annotation) arg_names.append(arg.variable.name()) arg_kinds.append(arg.kind) signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type) if tvar_def: signature.variables = [tvar_def] func = FuncDef(name, args, Block([PassStmt()])) func.info = info func.type = set_callable_name(signature, func) func._fullname = info.fullname() + '.' + name func.line = info.line info.names[name] = SymbolTableNode(MDEF, func, plugin_generated=True) info.defn.defs.body.append(func)
def add_method(funcname: str, ret: Type, args: List[Argument], name: Optional[str] = None, is_classmethod: bool = False, is_new: bool = False, ) -> None: if is_classmethod or is_new: first = [Argument(Var('cls'), TypeType.make_normalized(selftype), None, ARG_POS)] else: first = [Argument(Var('self'), selftype, None, ARG_POS)] args = first + args types = [arg.type_annotation for arg in args] items = [arg.variable.name() for arg in args] arg_kinds = [arg.kind for arg in args] assert None not in types signature = CallableType(cast(List[Type], types), arg_kinds, items, ret, function_type) signature.variables = [tvd] func = FuncDef(funcname, args, Block([])) func.info = info func.is_class = is_classmethod func.type = set_callable_name(signature, func) func._fullname = info.fullname() + '.' + funcname if is_classmethod: v = Var(funcname, func.type) v.is_classmethod = True v.info = info v._fullname = func._fullname dec = Decorator(func, [NameExpr('classmethod')], v) info.names[funcname] = SymbolTableNode(MDEF, dec) else: info.names[funcname] = SymbolTableNode(MDEF, func)
def visit_FunctionDef(self, n: ast35.FunctionDef) -> Node: args = self.transform_args(n.args, n.lineno) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] arg_types = None # type: List[Type] if n.type_comment is not None: try: func_type_ast = ast35.parse(n.type_comment, '<func_type>', 'func_type') except SyntaxError: raise TypeCommentParseError(TYPE_COMMENT_SYNTAX_ERROR, n.lineno) assert isinstance(func_type_ast, ast35.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance(func_type_ast.argtypes[0], ast35.Ellipsis)): arg_types = [a.type_annotation if a.type_annotation is not None else AnyType() for a in args] else: arg_types = [a if a is not None else AnyType() for a in TypeConverter(line=n.lineno).visit_list(func_type_ast.argtypes)] return_type = TypeConverter(line=n.lineno).visit(func_type_ast.returns) # add implicit self type if self.in_class() and len(arg_types) < len(args): arg_types.insert(0, AnyType()) else: arg_types = [a.type_annotation for a in args] return_type = TypeConverter(line=n.lineno).visit(n.returns) if isinstance(return_type, UnboundType): return_type.is_ret_type = True func_type = None if any(arg_types) or return_type: func_type = CallableType([a if a is not None else AnyType() for a in arg_types], arg_kinds, arg_names, return_type if return_type is not None else AnyType(), None) func_def = FuncDef(n.name, args, self.as_block(n.body, n.lineno), func_type) if func_type is not None: func_type.definition = func_def func_type.line = n.lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.visit_list(n.decorator_list), var) else: return func_def
def visit_callable_type(self, t: CallableType) -> Type: if isinstance(self.s, CallableType) and is_similar_callables( t, cast(CallableType, self.s)): return combine_similar_callables(t, cast(CallableType, self.s)) elif t.is_type_obj() and is_subtype(self.s, t.fallback): return t.fallback elif (t.is_type_obj() and isinstance(self.s, Instance) and cast(Instance, self.s).type == t.fallback): return t.fallback else: return self.default(self.s)
def infer_function_type_arguments(callee_type: CallableType, arg_types: List[Optional[Type]], arg_kinds: List[int], formal_to_actual: List[List[int]], strict: bool = True) -> List[Type]: """Infer the type arguments of a generic function. Return an array of lower bound types for the type variables -1 (at index 0), -2 (at index 1), etc. A lower bound is None if a value could not be inferred. Arguments: callee_type: the target generic function arg_types: argument types at the call site (each optional; if None, we are not considering this argument in the current pass) arg_kinds: nodes.ARG_* values for arg_types formal_to_actual: mapping from formal to actual variable indices """ # Infer constraints. constraints = infer_constraints_for_callable( callee_type, arg_types, arg_kinds, formal_to_actual) # Solve constraints. type_vars = callee_type.type_var_ids() return solve_constraints(type_vars, constraints, strict)
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 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 is_callable_subtype(left: CallableType, right: CallableType, ignore_return: bool = False) -> bool: """Is left a subtype of right?""" # TODO: Support named arguments, **args, etc. # 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 # Check argument types. if left.min_args > right.min_args: return False if left.is_var_arg: return is_var_arg_callable_subtype_helper(left, right) if right.is_var_arg: return False if len(left.arg_types) < len(right.arg_types): return False for i in range(len(right.arg_types)): if not is_subtype(right.arg_types[i], left.arg_types[i]): return False return True
def visit_callable_type(self, typ: CallableType) -> SnapshotItem: # FIX generics return ('CallableType', snapshot_types(typ.arg_types), snapshot_type(typ.ret_type), tuple(typ.arg_names), tuple(typ.arg_kinds), typ.is_type_obj(), typ.is_ellipsis_args)
def visit_FunctionDef(self, n): args = self.transform_args(n.args, n.lineno) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] if n.type_comment is not None: func_type_ast = typed_ast.parse(n.type_comment, '<func_type>', 'func_type') arg_types = [a if a is not None else AnyType() for a in TypeConverter(line=n.lineno).visit(func_type_ast.argtypes)] return_type = TypeConverter(line=n.lineno).visit(func_type_ast.returns) # add implicit self type if self.in_class and len(arg_types) < len(args): arg_types.insert(0, AnyType()) else: arg_types = [a.type_annotation for a in args] return_type = TypeConverter(line=n.lineno).visit(n.returns) func_type = None if any(arg_types) or return_type: func_type = CallableType([a if a is not None else AnyType() for a in arg_types], arg_kinds, arg_names, return_type if return_type is not None else AnyType(), None) func_def = FuncDef(n.name, args, self.as_block(n.body, n.lineno), func_type) if func_type is not None: func_type.definition = func_def if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.visit(n.decorator_list), var) else: return func_def
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 analyze_callable_type(self, t: UnboundType) -> Type: fallback = self.named_type('builtins.function') if len(t.args) == 0: # Callable (bare). Treat as Callable[..., Any]. any_type = AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) ret = CallableType([any_type, any_type], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], ret_type=any_type, fallback=fallback, is_ellipsis_args=True) elif len(t.args) == 2: ret_type = t.args[1] if isinstance(t.args[0], TypeList): # Callable[[ARG, ...], RET] (ordinary callable type) analyzed_args = self.analyze_callable_args(t.args[0]) if analyzed_args is None: return AnyType(TypeOfAny.from_error) args, kinds, names = analyzed_args ret = CallableType(args, kinds, names, ret_type=ret_type, fallback=fallback) elif isinstance(t.args[0], EllipsisType): # Callable[..., RET] (with literal ellipsis; accept arbitrary arguments) ret = CallableType([AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], ret_type=ret_type, fallback=fallback, is_ellipsis_args=True) else: self.fail('The first argument to Callable must be a list of types or "..."', t) return AnyType(TypeOfAny.from_error) else: self.fail('Please use "Callable[[<parameters>], <return type>]" or "Callable"', t) return AnyType(TypeOfAny.from_error) assert isinstance(ret, CallableType) return ret.accept(self)
def visit_callable_type(self, left: CallableType) -> bool: # FIX generics if isinstance(self.right, CallableType): cright = cast(CallableType, self.right) return (is_same_type(left.ret_type, cright.ret_type) and is_same_types(left.arg_types, cright.arg_types) and left.arg_names == cright.arg_names and left.arg_kinds == cright.arg_kinds and left.is_type_obj() == cright.is_type_obj()) else: return False
def visit_callable_type(self, left: CallableType) -> bool: # FIX generics if isinstance(self.right, CallableType): cright = self.right return (is_identical_type(left.ret_type, cright.ret_type) and is_identical_types(left.arg_types, cright.arg_types) and left.arg_names == cright.arg_names and left.arg_kinds == cright.arg_kinds and left.is_type_obj() == cright.is_type_obj() and left.is_ellipsis_args == cright.is_ellipsis_args) return False
def visit_callable_type(self, t: CallableType) -> Type: if isinstance(self.s, CallableType) and is_similar_callables(t, self.s): if is_equivalent(t, self.s): return combine_similar_callables(t, self.s) result = meet_similar_callables(t, self.s) if isinstance(result.ret_type, UninhabitedType): # Return a plain None or <uninhabited> instead of a weird function. return self.default(self.s) return result elif isinstance(self.s, TypeType) and t.is_type_obj() and not t.is_generic(): # In this case we are able to potentially produce a better meet. res = meet_types(self.s.item, t.ret_type) if not isinstance(res, (NoneType, UninhabitedType)): return TypeType.make_normalized(res) return self.default(self.s) 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 self.default(self.s)
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 visit_callable_type(self, typ: CallableType) -> None: for arg in typ.arg_types: arg.accept(self) typ.ret_type.accept(self) if typ.definition: # No need to fixup since this is just a cross-reference. typ.definition = self.replacements.get(typ.definition, typ.definition) # TODO: typ.fallback for tv in typ.variables: tv.upper_bound.accept(self) for value in tv.values: value.accept(self)
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 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 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 visit_callable_type(self, left: CallableType) -> bool: right = self.right if isinstance(right, CallableType): return is_callable_subtype(left, right) elif isinstance(right, Overloaded): return all(is_subtype(left, item) for item in right.items()) elif is_named_instance(right, 'builtins.object'): return True elif (is_named_instance(right, 'builtins.type') and left.is_type_obj()): return True else: return False
def visit_callable_type(self, typ: CallableType) -> None: for arg in typ.arg_types: arg.accept(self) typ.ret_type.accept(self) if typ.definition: # No need to fixup since this is just a cross-reference. typ.definition = self.replacements.get(typ.definition, typ.definition) # Fallback can be None for callable types that haven't been semantically analyzed. if typ.fallback is not None: typ.fallback.accept(self) for tv in typ.variables: tv.upper_bound.accept(self) for value in tv.values: value.accept(self)
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 visit_callable_type(self, left: CallableType) -> bool: right = self.right if isinstance(right, CallableType): return is_callable_subtype(left, right) elif isinstance(right, Overloaded): return all(is_subtype(left, item, self.check_type_parameter) for item in right.items()) elif isinstance(right, Instance): return is_subtype(left.fallback, right) elif isinstance(right, TypeType): # This is unsound, we don't check the __init__ signature. return left.is_type_obj() and is_subtype(left.ret_type, right.item) else: return False
def is_var_arg_callable_subtype_helper(left: CallableType, right: CallableType) -> bool: """Is left a subtype of right, assuming left has *args? See also is_callable_subtype for additional assumptions we can make. """ left_fixed = left.max_fixed_args() right_fixed = right.max_fixed_args() num_fixed_matching = min(left_fixed, right_fixed) for i in range(num_fixed_matching): if not is_subtype(right.arg_types[i], left.arg_types[i]): return False if not right.is_var_arg: for i in range(num_fixed_matching, len(right.arg_types)): if not is_subtype(right.arg_types[i], left.arg_types[-1]): return False return True else: for i in range(left_fixed, right_fixed): if not is_subtype(right.arg_types[i], left.arg_types[-1]): return False for i in range(right_fixed, left_fixed): if not is_subtype(right.arg_types[-1], left.arg_types[i]): return False return is_subtype(right.arg_types[-1], left.arg_types[-1])
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, left: CallableType) -> bool: right = self.right if isinstance(right, CallableType): return is_callable_subtype( left, right, ignore_pos_arg_names=False, use_proper_subtype=True) elif isinstance(right, Overloaded): return all(is_proper_subtype(left, item) for item in right.items()) elif isinstance(right, Instance): return is_proper_subtype(left.fallback, right) elif isinstance(right, TypeType): # This is unsound, we don't check the __init__ signature. return left.is_type_obj() and is_proper_subtype(left.ret_type, right.item) return False
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 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 join_similar_callables(t: CallableType, s: CallableType) -> CallableType: from mypy.meet import meet_types arg_types: List[Type] = [] for i in range(len(t.arg_types)): arg_types.append(meet_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 '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, arg_names=combine_arg_names(t, s), ret_type=join_types(t.ret_type, s.ret_type), fallback=fallback, name=None)
def analyze_none_member_access(name: str, typ: NoneType, mx: MemberContext) -> Type: is_python_3 = mx.chk.options.python_version[0] >= 3 # In Python 2 "None" has exactly the same attributes as "object". Python 3 adds a single # extra attribute, "__bool__". if is_python_3 and name == '__bool__': literal_false = LiteralType(False, fallback=mx.named_type('builtins.bool')) return CallableType(arg_types=[], arg_kinds=[], arg_names=[], ret_type=literal_false, fallback=mx.named_type('builtins.function')) elif mx.chk.should_suppress_optional_error([typ]): return AnyType(TypeOfAny.from_error) else: return _analyze_member_access(name, mx.named_type('builtins.object'), mx)
def visit_callable_type(self, left: CallableType) -> bool: right = self.right if isinstance(right, CallableType): return is_callable_subtype( left, right, ignore_pos_arg_names=self.ignore_pos_arg_names) elif isinstance(right, Overloaded): return all(is_subtype(left, item, self.check_type_parameter, ignore_pos_arg_names=self.ignore_pos_arg_names) for item in right.items()) elif isinstance(right, Instance): return is_subtype(left.fallback, right, ignore_pos_arg_names=self.ignore_pos_arg_names) elif isinstance(right, TypeType): # This is unsound, we don't check the __init__ signature. return left.is_type_obj() and is_subtype(left.ret_type, right.item) else: return False
def analyze_callable_type(self, t: UnboundType) -> Type: fallback = self.named_type('builtins.function') if len(t.args) == 0: # Callable (bare). Treat as Callable[..., Any]. any_type = AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) ret = CallableType([any_type, any_type], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], ret_type=any_type, fallback=fallback, is_ellipsis_args=True) elif len(t.args) == 2: ret_type = t.args[1] if isinstance(t.args[0], TypeList): # Callable[[ARG, ...], RET] (ordinary callable type) analyzed_args = self.analyze_callable_args(t.args[0]) if analyzed_args is None: return AnyType(TypeOfAny.from_error) args, kinds, names = analyzed_args ret = CallableType(args, kinds, names, ret_type=ret_type, fallback=fallback) elif isinstance(t.args[0], EllipsisType): # Callable[..., RET] (with literal ellipsis; accept arbitrary arguments) ret = CallableType( [AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], ret_type=ret_type, fallback=fallback, is_ellipsis_args=True) else: self.fail( 'The first argument to Callable must be a list of types or "..."', t) return AnyType(TypeOfAny.from_error) else: self.fail( 'Please use "Callable[[<parameters>], <return type>]" or "Callable"', t) return AnyType(TypeOfAny.from_error) assert isinstance(ret, CallableType) return ret.accept(self)
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 CallableType(arg_types, t.arg_kinds, t.arg_names, join_types(t.ret_type, s.ret_type), fallback, None, t.variables) return s
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())) cc = convert_class_tvars_to_func_tvars(c, len(initvars)) cc.is_classmethod_class = True return cc
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) if is_new and isinstance(init_type.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 build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance) -> TypeInfo: info = self.api.basic_new_typeinfo(name, base_type) info.is_newtype = True # Add __init__ method args = [Argument(Var('self'), NoneType(), None, ARG_POS), self.make_argument('item', old_type)] signature = CallableType( arg_types=[Instance(info, []), old_type], arg_kinds=[arg.kind for arg in args], arg_names=['self', 'item'], ret_type=NoneType(), fallback=self.api.named_type('__builtins__.function'), name=name) init_func = FuncDef('__init__', args, Block([]), typ=signature) init_func.info = info init_func._fullname = info.fullname + '.__init__' info.names['__init__'] = SymbolTableNode(MDEF, init_func) return info
def analyze_callable_type(self, t: UnboundType) -> Type: fallback = self.named_type('builtins.function') if len(t.args) == 0: # Callable (bare). Treat as Callable[..., Any]. any_type = AnyType(TypeOfAny.from_omitted_generics, line=t.line, column=t.column) ret = CallableType([any_type, any_type], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], ret_type=any_type, fallback=fallback, is_ellipsis_args=True) elif len(t.args) == 2: ret_type = t.args[1] if isinstance(t.args[0], TypeList): # Callable[[ARG, ...], RET] (ordinary callable type) analyzed_args = self.analyze_callable_args(t.args[0]) if analyzed_args is None: return AnyType(TypeOfAny.from_error) args, kinds, names = analyzed_args ret = CallableType(args, kinds, names, ret_type=ret_type, fallback=fallback) elif isinstance(t.args[0], EllipsisType): # Callable[..., RET] (with literal ellipsis; accept arbitrary arguments) ret = CallableType( [AnyType(TypeOfAny.explicit), AnyType(TypeOfAny.explicit)], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], ret_type=ret_type, fallback=fallback, is_ellipsis_args=True) else: self.fail(errorcode.THE_FIRST_ARGUMENT_TO_CALLABLE_MUST_BE(), t) return AnyType(TypeOfAny.from_error) else: self.fail(errorcode.USE_CALLABLE_TYPE_SYNTAX(), t) return AnyType(TypeOfAny.from_error) assert isinstance(ret, CallableType) return ret.accept(self)
def function_type(func: FuncBase, fallback: Instance) -> FunctionLike: if func.type: assert isinstance(func.type, FunctionLike) return func.type else: # Implicit type signature with dynamic types. if isinstance(func, FuncItem): return callable_type(func, fallback) else: # Broken overloads can have self.type set to None. # TODO: should we instead always set the type in semantic analyzer? assert isinstance(func, OverloadedFuncDef) any_type = AnyType(TypeOfAny.from_error) dummy = CallableType([any_type, any_type], [ARG_STAR, ARG_STAR2], [None, None], any_type, fallback, line=func.line, is_ellipsis_args=True) # Return an Overloaded, because some callers may expect that # an OverloadedFuncDef has an Overloaded type. return Overloaded([dummy])
def infer_function_type_arguments( callee_type: CallableType, arg_types: List[Type], arg_kinds: List[int], formal_to_actual: List[List[int]]) -> List[Type]: """Infer the type arguments of a generic function. Return an array of lower bound types for the type variables -1 (at index 0), -2 (at index 1), etc. A lower bound is None if a value could not be inferred. Arguments: callee_type: the target generic function arg_types: argument types at the call site arg_kinds: nodes.ARG_* values for arg_types formal_to_actual: mapping from formal to actual variable indices """ # Infer constraints. constraints = infer_constraints_for_callable(callee_type, arg_types, arg_kinds, formal_to_actual) # Solve constraints. type_vars = callee_type.type_var_ids() return solve_constraints(type_vars, constraints)
def type_object_type(check_untyped_defs: bool, info: TypeInfo, builtin_type: Callable[[str], Instance]) -> Type: """Return the type of a type object. For a generic type G with type variables T and S the type is generally of form Callable[..., G[T, S]] where ... are argument types for the __init__/__new__ method (without the self argument). Also, the fallback type will be 'type' instead of 'function'. """ init_method = info.get_method('__init__') if not init_method: # Must be an invalid class definition. return AnyType() else: fallback = builtin_type('builtins.type') if init_method.info.fullname() == 'builtins.object': # No non-default __init__ -> look at __new__ instead. new_method = info.get_method('__new__') if new_method and new_method.info.fullname() != 'builtins.object': # Found one! Get signature from __new__. return type_object_type_from_function(check_untyped_defs, new_method, info, fallback) # Both are defined by object. But if we've got a bogus # base class, we can't know for sure, so check for that. if info.fallback_to_any: # Construct a universal callable as the prototype. sig = CallableType(arg_types=[AnyType(), AnyType()], arg_kinds=[ARG_STAR, ARG_STAR2], arg_names=["_args", "_kwds"], ret_type=AnyType(), fallback=builtin_type('builtins.function')) return class_callable(sig, info, fallback, None) # Construct callable type based on signature of __init__. Adjust # return type and insert type arguments. return type_object_type_from_function(check_untyped_defs, init_method, info, fallback)
def callable_type(fdef: FuncItem, fallback: Instance, ret_type: Optional[Type] = None) -> CallableType: # TODO: somewhat unfortunate duplication with prepare_method_signature in semanal if fdef.info and not fdef.is_static and fdef.arg_names: self_type = fill_typevars(fdef.info) # type: Type if fdef.is_class or fdef.name == '__new__': self_type = TypeType.make_normalized(self_type) args = [self_type] + [AnyType(TypeOfAny.unannotated)] * (len(fdef.arg_names)-1) else: args = [AnyType(TypeOfAny.unannotated)] * len(fdef.arg_names) return CallableType( args, fdef.arg_kinds, [None if argument_elide_name(n) else n for n in fdef.arg_names], ret_type or AnyType(TypeOfAny.unannotated), fallback, name=fdef.name, line=fdef.line, column=fdef.column, implicit=True, )
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)) # We have to skip protocols, because it can can be a subtype of a return type # by accident. Like `Hashable` is a subtype of `object`. See #11799 and isinstance(default_ret_type, Instance) and not default_ret_type.type.is_protocol # 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 analyze_callable_type(self, t: UnboundType) -> Type: fallback = self.named_type('builtins.function') if len(t.args) == 0: # Callable (bare). Treat as Callable[..., Any]. ret = CallableType([AnyType(), AnyType()], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], ret_type=AnyType(), fallback=fallback, is_ellipsis_args=True) elif len(t.args) == 2: ret_type = t.args[1] if isinstance(t.args[0], TypeList): # Callable[[ARG, ...], RET] (ordinary callable type) analyzed_args = self.analyze_callable_args(t.args[0]) if analyzed_args is None: return AnyType() args, kinds, names = analyzed_args ret = CallableType(args, kinds, names, ret_type=ret_type, fallback=fallback) elif isinstance(t.args[0], EllipsisType): # Callable[..., RET] (with literal ellipsis; accept arbitrary arguments) ret = CallableType([AnyType(), AnyType()], [nodes.ARG_STAR, nodes.ARG_STAR2], [None, None], ret_type=ret_type, fallback=fallback, is_ellipsis_args=True) else: self.fail('The first argument to Callable must be a list of types or "..."', t) return AnyType() else: self.fail('Invalid function type', t) return AnyType() assert isinstance(ret, CallableType) return ret.accept(self)
def callable_type(fdef: FuncItem, fallback: Instance, ret_type: Optional[Type] = None) -> CallableType: # TODO: somewhat unfortunate duplication with prepare_method_signature in semanal if fdef.info and not fdef.is_static and fdef.arg_names: self_type: Type = fill_typevars(fdef.info) if fdef.is_class or fdef.name == '__new__': self_type = TypeType.make_normalized(self_type) args = [self_type] + [AnyType(TypeOfAny.unannotated)] * (len(fdef.arg_names)-1) else: args = [AnyType(TypeOfAny.unannotated)] * len(fdef.arg_names) return CallableType( args, fdef.arg_kinds, fdef.arg_names, ret_type or AnyType(TypeOfAny.unannotated), fallback, name=fdef.name, line=fdef.line, column=fdef.column, implicit=True, # We need this for better error messages, like missing `self` note: definition=fdef if isinstance(fdef, FuncDef) else None, )
def parse_signature(tokens: List[Token]) -> Tuple[CallableType, int]: """Parse signature of form (argtype, ...) -> ... Return tuple (signature type, token index). """ i = 0 if tokens[i].string != '(': raise TypeParseError(tokens[i], i) i += 1 arg_types = [] # type: List[Type] arg_kinds = [] # type: List[int] while tokens[i].string != ')': if tokens[i].string == '*': arg_kinds.append(nodes.ARG_STAR) i += 1 elif tokens[i].string == '**': arg_kinds.append(nodes.ARG_STAR2) i += 1 else: arg_kinds.append(nodes.ARG_POS) arg, i = parse_type(tokens, i) arg_types.append(arg) next = tokens[i].string if next not in ',)': raise TypeParseError(tokens[i], i) if next == ',': i += 1 i += 1 if tokens[i].string != '->': raise TypeParseError(tokens[i], i) i += 1 ret_type, i = parse_type(tokens, i) return CallableType(arg_types, arg_kinds, [None] * len(arg_types), ret_type, None), i
def type_object_type(info: TypeInfo, builtin_type: Callable[[str], Instance]) -> ProperType: """Return the type of a type object. For a generic type G with type variables T and S the type is generally of form Callable[..., G[T, S]] where ... are argument types for the __init__/__new__ method (without the self argument). Also, the fallback type will be 'type' instead of 'function'. """ # We take the type from whichever of __init__ and __new__ is first # in the MRO, preferring __init__ if there is a tie. init_method = info.get('__init__') new_method = info.get('__new__') if not init_method or not is_valid_constructor(init_method.node): # Must be an invalid class definition. return AnyType(TypeOfAny.from_error) # There *should* always be a __new__ method except the test stubs # lack it, so just copy init_method in that situation new_method = new_method or init_method if not is_valid_constructor(new_method.node): # Must be an invalid class definition. return AnyType(TypeOfAny.from_error) # The two is_valid_constructor() checks ensure this. assert isinstance(new_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) assert isinstance(init_method.node, (SYMBOL_FUNCBASE_TYPES, Decorator)) init_index = info.mro.index(init_method.node.info) new_index = info.mro.index(new_method.node.info) fallback = info.metaclass_type or builtin_type('builtins.type') if init_index < new_index: method = init_method.node # type: Union[FuncBase, Decorator] is_new = False elif init_index > new_index: method = new_method.node is_new = True else: if init_method.node.info.fullname == 'builtins.object': # Both are defined by object. But if we've got a bogus # base class, we can't know for sure, so check for that. if info.fallback_to_any: # Construct a universal callable as the prototype. any_type = AnyType(TypeOfAny.special_form) sig = CallableType(arg_types=[any_type, any_type], arg_kinds=[ARG_STAR, ARG_STAR2], arg_names=["_args", "_kwds"], ret_type=any_type, fallback=builtin_type('builtins.function')) return class_callable(sig, info, fallback, None, is_new=False) # Otherwise prefer __init__ in a tie. It isn't clear that this # is the right thing, but __new__ caused problems with # typeshed (#5647). method = init_method.node is_new = False # Construct callable type based on signature of __init__. Adjust # return type and insert type arguments. if isinstance(method, FuncBase): t = function_type(method, fallback) else: assert isinstance(method.type, ProperType) assert isinstance(method.type, FunctionLike) # is_valid_constructor() ensures this t = method.type return type_object_type_from_function(t, info, method.info, fallback, is_new)
def visit_FunctionDef(self, n: ast27.FunctionDef) -> Statement: self.class_and_function_stack.append('F') lineno = n.lineno converter = TypeConverter(self.errors, line=lineno, override_column=n.col_offset, assume_str_is_unicode=self.unicode_literals) args, decompose_stmts = self.transform_args(n.args, lineno) if special_function_elide_names(n.name): for arg in args: arg.pos_only = True arg_kinds = [arg.kind for arg in args] arg_names = [ None if arg.pos_only else arg.variable.name for arg in args ] arg_types: List[Optional[Type]] = [] type_comment = n.type_comment if (n.decorator_list and any( is_no_type_check_decorator(d) for d in n.decorator_list)): arg_types = [None] * len(args) return_type = None elif type_comment is not None and len(type_comment) > 0: try: func_type_ast = ast3_parse(type_comment, '<func_type>', 'func_type') assert isinstance(func_type_ast, ast3.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance( func_type_ast.argtypes[0], ast3.Ellipsis)): arg_types = [ a.type_annotation if a.type_annotation is not None else AnyType(TypeOfAny.unannotated) for a in args ] else: # PEP 484 disallows both type annotations and type comments if any(a.type_annotation is not None for a in args): self.fail(message_registry.DUPLICATE_TYPE_SIGNATURES, lineno, n.col_offset) arg_types = [ a if a is not None else AnyType(TypeOfAny.unannotated) for a in converter.translate_expr_list( func_type_ast.argtypes) ] return_type = converter.visit(func_type_ast.returns) # add implicit self type if self.in_method_scope() and len(arg_types) < len(args): arg_types.insert(0, AnyType(TypeOfAny.special_form)) except SyntaxError: stripped_type = type_comment.split("#", 2)[0].strip() err_msg = '{} "{}"'.format(TYPE_COMMENT_SYNTAX_ERROR, stripped_type) self.fail(err_msg, lineno, n.col_offset) arg_types = [AnyType(TypeOfAny.from_error)] * len(args) return_type = AnyType(TypeOfAny.from_error) else: arg_types = [a.type_annotation for a in args] return_type = converter.visit(None) for arg, arg_type in zip(args, arg_types): self.set_type_optional(arg_type, arg.initializer) func_type = None if any(arg_types) or return_type: if len(arg_types) != 1 and any( isinstance(t, EllipsisType) for t in arg_types): self.fail( "Ellipses cannot accompany other argument types " "in function type signature", lineno, n.col_offset) elif len(arg_types) > len(arg_kinds): self.fail('Type signature has too many arguments', lineno, n.col_offset, blocker=False) elif len(arg_types) < len(arg_kinds): self.fail('Type signature has too few arguments', lineno, n.col_offset, blocker=False) else: any_type = AnyType(TypeOfAny.unannotated) func_type = CallableType( [a if a is not None else any_type for a in arg_types], arg_kinds, arg_names, return_type if return_type is not None else any_type, _dummy_fallback) body = self.as_required_block(n.body, lineno) if decompose_stmts: body.body = decompose_stmts + body.body func_def = FuncDef(n.name, args, body, func_type) if isinstance(func_def.type, CallableType): # semanal.py does some in-place modifications we want to avoid func_def.unanalyzed_type = func_def.type.copy_modified() if func_type is not None: func_type.definition = func_def func_type.line = lineno if n.decorator_list: var = Var(func_def.name) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) dec = Decorator(func_def, self.translate_expr_list(n.decorator_list), var) dec.set_line(lineno, n.col_offset) retval: Statement = dec else: # Overrides set_line -- can't use self.set_line func_def.set_line(lineno, n.col_offset) retval = func_def self.class_and_function_stack.pop() return retval
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 visit_FunctionDef(self, n: ast27.FunctionDef) -> Statement: converter = TypeConverter(self.errors, line=n.lineno) args, decompose_stmts = self.transform_args(n.args, n.lineno) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] # type: List[Optional[str]] arg_names = [None if argument_elide_name(name) else name for name in arg_names] if special_function_elide_names(n.name): arg_names = [None] * len(arg_names) arg_types = [] # type: List[Optional[Type]] if (n.decorator_list and any(is_no_type_check_decorator(d) for d in n.decorator_list)): arg_types = [None] * len(args) return_type = None elif n.type_comment is not None and len(n.type_comment) > 0: try: func_type_ast = ast3.parse(n.type_comment, '<func_type>', 'func_type') assert isinstance(func_type_ast, ast3.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance(func_type_ast.argtypes[0], ast3.Ellipsis)): arg_types = [a.type_annotation if a.type_annotation is not None else AnyType(TypeOfAny.unannotated) for a in args] else: # PEP 484 disallows both type annotations and type comments if any(a.type_annotation is not None for a in args): self.fail(messages.DUPLICATE_TYPE_SIGNATURES, n.lineno, n.col_offset) arg_types = [a if a is not None else AnyType(TypeOfAny.unannotated) for a in converter.translate_expr_list(func_type_ast.argtypes)] return_type = converter.visit(func_type_ast.returns) # add implicit self type if self.in_class() and len(arg_types) < len(args): arg_types.insert(0, AnyType(TypeOfAny.special_form)) except SyntaxError: self.fail(TYPE_COMMENT_SYNTAX_ERROR, n.lineno, n.col_offset) arg_types = [AnyType(TypeOfAny.from_error)] * len(args) return_type = AnyType(TypeOfAny.from_error) else: arg_types = [a.type_annotation for a in args] return_type = converter.visit(None) for arg, arg_type in zip(args, arg_types): self.set_type_optional(arg_type, arg.initializer) func_type = None if any(arg_types) or return_type: if len(arg_types) != 1 and any(isinstance(t, EllipsisType) for t in arg_types): self.fail("Ellipses cannot accompany other argument types " "in function type signature.", n.lineno, 0) elif len(arg_types) > len(arg_kinds): self.fail('Type signature has too many arguments', n.lineno, 0) elif len(arg_types) < len(arg_kinds): self.fail('Type signature has too few arguments', n.lineno, 0) else: any_type = AnyType(TypeOfAny.unannotated) func_type = CallableType([a if a is not None else any_type for a in arg_types], arg_kinds, arg_names, return_type if return_type is not None else any_type, _dummy_fallback) body = self.as_required_block(n.body, n.lineno) if decompose_stmts: body.body = decompose_stmts + body.body func_def = FuncDef(n.name, args, body, func_type) if func_type is not None: func_type.definition = func_def func_type.line = n.lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.translate_expr_list(n.decorator_list), var) else: return func_def
def visit_callable_type(self, template: CallableType) -> List[Constraint]: if isinstance(self.actual, CallableType): res: List[Constraint] = [] cactual = self.actual param_spec = template.param_spec() if param_spec is None: # FIX verify argument counts # FIX what if one of the functions is generic # We can't infer constraints from arguments if the template is Callable[..., T] # (with literal '...'). if not template.is_ellipsis_args: # The lengths should match, but don't crash (it will error elsewhere). for t, a in zip(template.arg_types, cactual.arg_types): # Negate direction due to function argument type contravariance. res.extend( infer_constraints(t, a, neg_op(self.direction))) else: # TODO: Direction # TODO: Deal with arguments that come before param spec ones? res.append( Constraint(param_spec.id, SUBTYPE_OF, cactual.copy_modified(ret_type=NoneType()))) template_ret_type, cactual_ret_type = template.ret_type, cactual.ret_type if template.type_guard is not None: template_ret_type = template.type_guard if cactual.type_guard is not None: cactual_ret_type = cactual.type_guard res.extend( infer_constraints(template_ret_type, cactual_ret_type, self.direction)) return res elif isinstance(self.actual, AnyType): param_spec = template.param_spec() any_type = AnyType(TypeOfAny.from_another_any, source_any=self.actual) if param_spec is None: # FIX what if generic res = self.infer_against_any(template.arg_types, self.actual) else: res = [ Constraint( param_spec.id, SUBTYPE_OF, callable_with_ellipsis(any_type, any_type, template.fallback)) ] res.extend( infer_constraints(template.ret_type, any_type, self.direction)) return res elif isinstance(self.actual, Overloaded): return self.infer_against_overloaded(self.actual, template) elif isinstance(self.actual, TypeType): return infer_constraints(template.ret_type, self.actual.item, self.direction) elif isinstance(self.actual, Instance): # Instances with __call__ method defined are considered structural # subtypes of Callable with a compatible signature. call = mypy.subtypes.find_member('__call__', self.actual, self.actual, is_operator=True) if call: return infer_constraints(template, call, self.direction) else: return [] else: return []
def convert_class_tvars_to_func_tvars(callable: CallableType, num_func_tvars: int) -> CallableType: return cast(CallableType, callable.accept(TvarTranslator(num_func_tvars)))
def callable(self, *a: Type) -> CallableType: """callable(a1, ..., an, r) constructs a callable with argument types a1, ... an and return type r. """ return CallableType(list(a[:-1]), [ARG_POS] * (len(a) - 1), [None] * (len(a) - 1), a[-1], self.function)
def _add_bool_dunder(self, type_info: TypeInfo) -> None: signature = CallableType([], [], [], Instance(self.bool_type_info, []), self.function) bool_func = FuncDef('__bool__', [], Block([])) bool_func.type = set_callable_name(signature, bool_func) type_info.names[bool_func.name] = SymbolTableNode(MDEF, bool_func)
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) -> Type: # We must preserve the fallback type for overload resolution to work. ret_type = NoneTyp() # type: Type return CallableType([], [], [], ret_type, t.fallback)
def do_func_def(self, n: Union[ast3.FunctionDef, ast3.AsyncFunctionDef], is_coroutine: bool = False) -> Union[FuncDef, Decorator]: """Helper shared between visit_FunctionDef and visit_AsyncFunctionDef.""" no_type_check = bool( n.decorator_list and any(is_no_type_check_decorator(d) for d in n.decorator_list)) args = self.transform_args(n.args, n.lineno, no_type_check=no_type_check) arg_kinds = [arg.kind for arg in args] arg_names = [arg.variable.name() for arg in args] # type: List[Optional[str]] arg_names = [ None if argument_elide_name(name) else name for name in arg_names ] if special_function_elide_names(n.name): arg_names = [None] * len(arg_names) arg_types = None # type: List[Type] if no_type_check: arg_types = [None] * len(args) return_type = None elif n.type_comment is not None: try: func_type_ast = ast3.parse(n.type_comment, '<func_type>', 'func_type') assert isinstance(func_type_ast, ast3.FunctionType) # for ellipsis arg if (len(func_type_ast.argtypes) == 1 and isinstance( func_type_ast.argtypes[0], ast3.Ellipsis)): if n.returns: # PEP 484 disallows both type annotations and type comments self.fail(messages.DUPLICATE_TYPE_SIGNATURES, n.lineno, n.col_offset) arg_types = [ a.type_annotation if a.type_annotation is not None else AnyType() for a in args ] else: # PEP 484 disallows both type annotations and type comments if n.returns or any(a.type_annotation is not None for a in args): self.fail(messages.DUPLICATE_TYPE_SIGNATURES, n.lineno, n.col_offset) translated_args = (TypeConverter( self.errors, line=n.lineno).translate_expr_list( func_type_ast.argtypes)) arg_types = [ a if a is not None else AnyType() for a in translated_args ] return_type = TypeConverter(self.errors, line=n.lineno).visit( func_type_ast.returns) # add implicit self type if self.in_class() and len(arg_types) < len(args): arg_types.insert(0, AnyType()) except SyntaxError: self.fail(TYPE_COMMENT_SYNTAX_ERROR, n.lineno, n.col_offset) arg_types = [AnyType()] * len(args) return_type = AnyType() else: arg_types = [a.type_annotation for a in args] return_type = TypeConverter(self.errors, line=n.lineno).visit(n.returns) for arg, arg_type in zip(args, arg_types): self.set_type_optional(arg_type, arg.initializer) if isinstance(return_type, UnboundType): return_type.is_ret_type = True func_type = None if any(arg_types) or return_type: if len(arg_types) != 1 and any( isinstance(t, EllipsisType) for t in arg_types): self.fail( "Ellipses cannot accompany other argument types " "in function type signature.", n.lineno, 0) elif len(arg_types) > len(arg_kinds): self.fail('Type signature has too many arguments', n.lineno, 0) elif len(arg_types) < len(arg_kinds): self.fail('Type signature has too few arguments', n.lineno, 0) else: func_type = CallableType( [ a if a is not None else AnyType(implicit=True) for a in arg_types ], arg_kinds, arg_names, return_type if return_type is not None else AnyType( implicit=True), None) func_def = FuncDef(n.name, args, self.as_block(n.body, n.lineno), func_type) if is_coroutine: # A coroutine is also a generator, mostly for internal reasons. func_def.is_generator = func_def.is_coroutine = True if func_type is not None: func_type.definition = func_def func_type.line = n.lineno if n.decorator_list: var = Var(func_def.name()) var.is_ready = False var.set_line(n.decorator_list[0].lineno) func_def.is_decorated = True func_def.set_line(n.lineno + len(n.decorator_list)) func_def.body.set_line(func_def.get_line()) return Decorator(func_def, self.translate_expr_list(n.decorator_list), var) else: return func_def