def test_callable_type(self): c = Callable([self.x, self.y], [ARG_POS, ARG_POS], [None, None], AnyType(), False) assert_equal(str(c), 'def (X?, Y?) -> Any') c2 = Callable([], [], [], Void(None), False) assert_equal(str(c2), 'def ()')
def test_generic_function_type(self): c = Callable([self.x, self.y], [ARG_POS, ARG_POS], [None, None], self.y, False, None, [TypeVarDef('X', -1, None)]) assert_equal(str(c), 'def [X] (X?, Y?) -> Y?') v = [TypeVarDef('Y', -1, None), TypeVarDef('X', -2, None)] c2 = Callable([], [], [], Void(None), False, None, v) assert_equal(str(c2), 'def [Y, X] ()')
def test_callable_type_with_default_args(self): c = Callable([self.x, self.y], [ARG_POS, ARG_OPT], [None, None], AnyType(), False) assert_equal(str(c), 'def (X?, Y? =) -> Any') c2 = Callable([self.x, self.y], [ARG_OPT, ARG_OPT], [None, None], AnyType(), False) assert_equal(str(c2), 'def (X? =, Y? =) -> Any')
def test_callable_type_with_var_args(self): c = Callable([self.x], [ARG_STAR], [None], AnyType(), False) assert_equal(str(c), 'def (*X?) -> Any') c2 = Callable([self.x, self.y], [ARG_POS, ARG_STAR], [None, None], AnyType(), False) assert_equal(str(c2), 'def (X?, *Y?) -> Any') c3 = Callable([self.x, self.y], [ARG_OPT, ARG_STAR], [None, None], AnyType(), False) assert_equal(str(c3), 'def (X? =, *Y?) -> Any')
def callable(self, *a): """callable(a1, ..., an, r) constructs a callable with argument types a1, ... an and return type r. """ n = len(a) - 1 return Callable(a[:-1], [ARG_POS] * n, [None] * n, a[-1], False)
def visit_callable(self, t: Callable) -> Type: res = Callable(self.anal_array(t.arg_types), t.arg_kinds, t.arg_names, t.ret_type.accept(self), t.is_type_obj(), t.name, self.anal_var_defs(t.variables), self.anal_bound_vars(t.bound_vars), t.line, t.repr) return res
def callable_type(self, *a): """callable_type(a1, ..., an, r) constructs a callable with argument types a1, ... an and return type r, and which represents a type. """ return Callable(a[:-1], [ARG_POS] * (len(a) - 1), [None] * (len(a) - 1), a[-1], True)
def dynamic_sig(sig: Callable) -> Callable: """Translate callable type to type erased (dynamically-typed) callable. Preserve the number and kinds of arguments. """ return Callable([AnyType()] * len(sig.arg_types), sig.arg_kinds, sig.arg_names, AnyType(), sig.is_type_obj())
def add_class_tvars(t: Type, info: TypeInfo, is_classmethod: bool, builtin_type: Function[[str], Instance]) -> Type: if isinstance(t, Callable): # TODO: Should we propagate type variable values? vars = [ TypeVarDef(n, i + 1, None, builtin_type('builtins.object')) for i, n in enumerate(info.type_vars) ] arg_types = t.arg_types arg_kinds = t.arg_kinds arg_names = t.arg_names if is_classmethod: arg_types = arg_types[1:] arg_kinds = arg_kinds[1:] arg_names = arg_names[1:] return Callable(arg_types, arg_kinds, arg_names, t.ret_type, t.fallback, t.name, vars + t.variables, t.bound_vars, t.line, None) elif isinstance(t, Overloaded): return Overloaded([ cast(Callable, add_class_tvars(i, info, is_classmethod, builtin_type)) for i in t.items() ]) return t
def make_setter_wrapper(self, name: str, typ: Type) -> FuncDef: """Create a setter wrapper for a data attribute. The setter will be of this form: . def set$name(self: C, name: typ) -> None: . self.name! = name """ scope = self.make_scope() selft = self.self_type() selfv = scope.add('self', selft) namev = scope.add(name, typ) lvalue = MemberExpr(scope.name_expr('self'), name, direct=True) rvalue = scope.name_expr(name) ret = AssignmentStmt([lvalue], rvalue) wrapper_name = 'set$' + name sig = Callable([selft, typ], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Void(), False) fdef = FuncDef(wrapper_name, [selfv, namev], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Block([ret]), sig) fdef.info = self.tf.type_context() return fdef
def parse_signature(tokens: List[Token]) -> Tuple[Callable, int]: """Parse signature of form (...) -> ... Return tuple (signature type, token index). """ i = 0 if tokens[i].string != '(': raise TypeParseError(tokens[i], i) i += 1 arg_types = List[Type]() arg_kinds = 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 != '-' or tokens[i + 1].string != '>': raise TypeParseError(tokens[i], i) i += 2 ret_type, i = parse_type(tokens, i) return Callable(arg_types, arg_kinds, [None] * len(arg_types), ret_type, False), i
def add_arg_type_after_self(t: Callable, arg_type: Type) -> Callable: """Add an argument with the given type to a callable type after 'self'.""" return Callable([t.arg_types[0], arg_type] + t.arg_types[1:], [t.arg_kinds[0], nodes.ARG_POS] + t.arg_kinds[1:], [t.arg_names[0], None] + t.arg_names[1:], t.ret_type, t.is_type_obj(), t.name, t.variables, t.bound_vars, t.line, None)
def type_callable(self, *a): """type_callable(a1, ..., an, r) constructs a callable with argument types a1, ... an and return type r, and which represents a type. """ n = len(a) - 1 return Callable(a[:-1], [ARG_POS] * n, [None] * n, a[-1], self.fx.type_type)
def visit_callable(self, t: Callable) -> Type: res = Callable(self.anal_array(t.arg_types), t.arg_kinds, t.arg_names, t.ret_type.accept(self), self.builtin_type('builtins.function'), t.name, self.anal_var_defs(t.variables), self.anal_bound_vars(t.bound_vars), t.line, t.repr) return res
def callable_var_arg(self, min_args, *a): """callable_var_arg(min_args, a1, ..., an, r) constructs a callable with argument types a1, ... *an and return type r. """ n = len(a) - 1 return Callable(a[:-1], [ARG_POS] * min_args + [ARG_OPT] * (n - 1 - min_args) + [ARG_STAR], [None] * n, a[-1], False)
def callable_default(self, min_args, *a): """callable_default(min_args, a1, ..., an, r) constructs a callable with argument types a1, ... an and return type r, with min_args mandatory fixed arguments. """ n = len(a) - 1 return Callable(a[:-1], [ARG_POS] * min_args + [ARG_OPT] * (n - min_args), [None] * n, a[-1], False)
def fix_bound_init_tvars(self, callable: Callable, typ: Instance) -> Callable: """Replace bound type vars of callable with args from instance type.""" a = [] # type: List[Tuple[int, Type]] for i in range(len(typ.args)): a.append((i + 1, typ.args[i])) return Callable(callable.arg_types, callable.arg_kinds, callable.arg_names, callable.ret_type, callable.is_type_obj(), callable.name, callable.variables, a)
def combine_similar_callables(t: Callable, s: Callable, basic: BasicTypes) -> Callable: 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], basic)) # TODO kinds and argument names return Callable(arg_types, t.arg_kinds, t.arg_names, join_types(t.ret_type, s.ret_type, basic), t.is_type_obj() and s.is_type_obj(), None, t.variables) return s
def analyze_function_type(self, t: UnboundType) -> Type: if len(t.args) != 2: self.fail('Invalid function type', t) if not isinstance(t.args[0], TypeList): self.fail('Invalid function type', t) return AnyType() args = (cast(TypeList, t.args[0])).items return Callable(self.anal_array(args), [nodes.ARG_POS] * len(args), [None] * len(args), ret_type=t.args[1].accept(self), fallback=self.builtin_type('builtins.function'))
def callable(self, vars, *a) -> Callable: """callable(args, a1, ..., an, r) constructs a callable with argument types a1, ... an and return type r and type arguments vars. """ tv = [] # type: List[TypeVarDef] n = -1 for v in vars: tv.append(TypeVarDef(v, n, None)) n -= 1 return Callable(a[:-1], [ARG_POS] * (len(a) - 1), [None] * (len(a) - 1), a[-1], False, None, tv)
def add_class_tvars(t: Type, info: TypeInfo) -> Type: if isinstance(t, Callable): vars = [ TypeVarDef(n, i + 1, None) for i, n in enumerate(info.type_vars) ] return Callable(t.arg_types, t.arg_kinds, t.arg_names, t.ret_type, t.is_type_obj(), t.name, vars + t.variables, t.bound_vars, t.line, None) elif isinstance(t, Overloaded): return Overloaded( [cast(Callable, add_class_tvars(i, info)) for i in t.items()]) return t
def class_callable(init_type: Callable, info: TypeInfo) -> Callable: """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)) initvars = init_type.variables variables.extend(initvars) c = Callable(init_type.arg_types, init_type.arg_kinds, init_type.arg_names, self_type(info), True, None, variables).with_name('"{}"'.format(info.name())) return convert_class_tvars_to_func_tvars(c, len(initvars))
def apply_generic_arguments(callable: Callable, 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(arg_types, callable.arg_kinds, callable.arg_names, expand_type(callable.ret_type, id_to_type), callable.fallback, callable.name, remaining_tvars, callable.bound_vars + bound_vars, callable.line, callable.repr)
def make_generic_wrapper_init(self, info: TypeInfo) -> FuncDef: """Build constructor of a generic wrapper class.""" nslots = num_slots(info) cdefs = [] # type: List[Node] # Build superclass constructor call. base = info.mro[1] if base.fullname() != 'builtins.object' and self.tf.is_java: s = SuperExpr('__init__') cargs = [NameExpr('__o')] # type: List[Node] for n in range(num_slots(base)): cargs.append(NameExpr(tvar_arg_name(n + 1))) for n in range(num_slots(base)): cargs.append(NameExpr(tvar_arg_name(n + 1, BOUND_VAR))) c = CallExpr(s, cargs, [nodes.ARG_POS] * len(cargs)) cdefs.append(ExpressionStmt(c)) # Create initialization of the wrapped object. cdefs.append(AssignmentStmt([MemberExpr( self_expr(), self.object_member_name(info), direct=True)], NameExpr('__o'))) # Build constructor arguments. args = [Var('self'), Var('__o')] init = [None, None] # type: List[Node] for alt in [False, BOUND_VAR]: for n in range(nslots): args.append(Var(tvar_arg_name(n + 1, alt))) init.append(None) nargs = nslots * 2 + 2 fdef = FuncDef('__init__', args, [nodes.ARG_POS] * nargs, init, Block(cdefs), Callable( [AnyType()] * nargs, [nodes.ARG_POS] * nargs, [None] * nargs, Void(), is_type_obj=False)) fdef.info = info self.make_wrapper_slot_initializer(fdef) return fdef
def combine_similar_callables(t: Callable, s: Callable) -> Callable: 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 Callable(arg_types, t.arg_kinds, t.arg_names, join_types(t.ret_type, s.ret_type), fallback, None, t.variables) return s
def make_type_object_wrapper(self, tdef: ClassDef) -> FuncDef: """Construct dynamically typed wrapper function for a class. It simple calls the type object and returns the result. """ # TODO keyword args, default args and varargs # TODO overloads type_sig = cast(Callable, type_object_type(tdef.info, None)) type_sig = cast(Callable, erasetype.erase_typevars(type_sig)) init = cast(FuncDef, tdef.info.get_method('__init__')) arg_kinds = type_sig.arg_kinds # The wrapper function has a dynamically typed signature. wrapper_sig = Callable( [AnyType()] * len(arg_kinds), arg_kinds, [None] * len(arg_kinds), AnyType(), False) n = NameExpr(tdef.name) # TODO full name args = self.func_tf.call_args( init.args[1:], type_sig, wrapper_sig, True, False) call = CallExpr(n, args, arg_kinds) ret = ReturnStmt(call) fdef = FuncDef(tdef.name + self.tf.dynamic_suffix(), init.args[1:], arg_kinds, [None] * len(arg_kinds), Block([ret])) fdef.type = wrapper_sig return fdef
def make_getter_wrapper(self, name: str, typ: Type) -> FuncDef: """Create a getter wrapper for a data attribute. The getter will be of this form: . def $name*(self: C) -> type: . return self.name! """ scope = self.make_scope() selft = self.self_type() selfv = scope.add('self', selft) member_expr = MemberExpr(scope.name_expr('self'), name, direct=True) ret = ReturnStmt(member_expr) wrapper_name = '$' + name sig = Callable([selft], [nodes.ARG_POS], [None], typ, False) fdef = FuncDef(wrapper_name, [selfv], [nodes.ARG_POS], [None], Block([ret]), sig) fdef.info = self.tf.type_context() return fdef
def make_dynamic_getter_wrapper(self, name: str, typ: Type) -> FuncDef: """Create a dynamically-typed getter wrapper for a data attribute. The getter will be of this form: . def $name*(self: C) -> Any: . return {Any <= typ self.name!} """ scope = self.make_scope() selft = self.self_type() selfv = scope.add('self', selft) member_expr = MemberExpr(scope.name_expr('self'), name, direct=True) coerce_expr = coerce(member_expr, AnyType(), typ, self.tf.type_context()) ret = ReturnStmt(coerce_expr) wrapper_name = '$' + name + self.tf.dynamic_suffix() sig = Callable([selft], [nodes.ARG_POS], [None], AnyType(), False) return FuncDef(wrapper_name, [selfv], [nodes.ARG_POS], [None], Block([ret]), sig)
def make_dynamic_setter_wrapper(self, name: str, typ: Type) -> FuncDef: """Create a dynamically-typed setter wrapper for a data attribute. The setter will be of this form: . def set$name*(self: C, name; Any) -> None: . self.name! = {typ name} """ lvalue = MemberExpr(self_expr(), name, direct=True) name_expr = NameExpr(name) rvalue = coerce(name_expr, typ, AnyType(), self.tf.type_context()) ret = AssignmentStmt([lvalue], rvalue) wrapper_name = 'set$' + name + self.tf.dynamic_suffix() selft = self_type(self.tf.type_context()) sig = Callable([selft, AnyType()], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Void(), False) return FuncDef(wrapper_name, [Var('self'), Var(name)], [nodes.ARG_POS, nodes.ARG_POS], [None, None], Block([ret]), sig)
def visit_callable(self, t: Callable) -> Type: return Callable(self.expand_types(t.arg_types), t.arg_kinds, t.arg_names, t.ret_type.accept(self), t.is_type_obj(), t.name, t.variables, self.expand_bound_vars(t.bound_vars), t.line, t.repr)