def test_call_with_kwonly_args(self): f = self._make_func( param_names=("test",), kwonly_params=("a", "b"), annotations={ "test": self._ctx.convert.str_type, "a": self._ctx.convert.str_type, "b": self._ctx.convert.str_type }) kwargs = abstract.Dict(self._ctx) kwargs.update( self._ctx.root_node, { "a": self._ctx.convert.build_string(self._ctx.root_node, "2"), "b": self._ctx.convert.build_string(self._ctx.root_node, "3") }) kwargs = kwargs.to_variable(self._ctx.root_node) args = function.Args( posargs=(self._ctx.convert.build_string(self._ctx.root_node, "1"),), namedargs=abstract.Dict(self._ctx), starstarargs=kwargs) f.call(self._ctx.root_node, f, args) kwargs = abstract.Dict(self._ctx) kwargs.update( self._ctx.root_node, {"b": self._ctx.convert.build_string(self._ctx.root_node, "3")}) kwargs = kwargs.to_variable(self._ctx.root_node) args = function.Args( posargs=(self._ctx.convert.build_string(self._ctx.root_node, "1"), self._ctx.convert.build_int(self._ctx.root_node)), namedargs=abstract.Dict(self._ctx), starstarargs=kwargs) self.assertRaises(function.MissingParameter, f.call, self._ctx.root_node, f, args)
def test_call_with_all_args(self): f = self._make_func( param_names=("a", "b", "c"), varargs_name="arg", kwargs_name="kwarg", defaults=(self._ctx.convert.build_int(self._ctx.root_node),), annotations={ "a": self._ctx.convert.str_type, "b": self._ctx.convert.int_type, "c": self._ctx.convert.int_type, "arg": self._ctx.convert.primitive_classes[float], "kwarg": self._ctx.convert.primitive_classes[bool] }) posargs = (self._ctx.convert.build_string(self._ctx.root_node, "1"), self._ctx.convert.build_int(self._ctx.root_node)) float_inst = self._ctx.convert.primitive_class_instances[float] stararg = self._ctx.convert.build_tuple( self._ctx.root_node, (float_inst.to_variable(self._ctx.root_node),)) namedargs = abstract.Dict(self._ctx) kwarg = abstract.Dict(self._ctx) kwarg.update( self._ctx.root_node, { "x": self._ctx.convert.build_bool(self._ctx.root_node), "y": self._ctx.convert.build_bool(self._ctx.root_node) }) kwarg = kwarg.to_variable(self._ctx.root_node) args = function.Args(posargs, namedargs, stararg, kwarg) f.call(self._ctx.root_node, f, args)
def assert_call(self, expected, left, right): """Check that call() returned the desired results. Args: expected: A dict from values to source sets, where a source set is represented by the sorted binding names separated by spaces, for example "left:0 right:1" would indicate binding #0 of variable "left" and binding #1 of variable "right". left: A Variable to use as the first arg to call(). right: A Variable to use as the second arg to call(). """ name_map = {left: "left", right: "right"} node, result = self._is_instance.call( self._node, None, function.Args( (left, right), self.new_dict(), None, None)) self.assertIn(node, self._node.outgoing) result_map = {} # Turning source sets into canonical string representations of the binding # names makes it much easier to debug failures. for b in result.bindings: terms = set() for o in b.origins: self.assertEqual(node, o.where) for sources in o.source_sets: terms.add(" ".join(sorted( "%s:%d" % (name_map[b.variable], b.variable.bindings.index(b)) for b in sources))) result_map[b.data] = terms self.assertEqual(expected, result_map)
def test_call_with_wrong_arg_count(self): f = self._simple_sig([self._ctx.convert.int_type]) args = function.Args( posargs=(self._ctx.convert.build_int(self._ctx.root_node), self._ctx.convert.build_int(self._ctx.root_node))) self.assertRaises(function.WrongArgCount, f.call, self._ctx.root_node, f, args)
def _lookup_from_mro_and_handle_descriptors(self, node, cls, name, valself, skip): attr = self._lookup_from_mro(node, cls, name, valself, skip) if not attr.bindings: return node, None if isinstance(cls, abstract.InterpreterClass): result = self.ctx.program.NewVariable() nodes = [] # Deal with descriptors as a potential additional level of indirection. for v in attr.bindings: value = v.data if (isinstance(value, special_builtins.PropertyInstance) and valself and valself.data == cls): node2, getter = node, None else: node2, getter = self.get_attribute(node, value, "__get__", v) if getter is not None: posargs = [] if valself and valself.data != cls: posargs.append(valself.AssignToNewVariable()) else: posargs.append(self.ctx.convert.none.to_variable(node)) posargs.append(cls.to_variable(node)) node2, get_result = function.call_function( self.ctx, node2, getter, function.Args(tuple(posargs))) for getter in get_result.bindings: result.AddBinding(getter.data, [getter], node2) else: result.AddBinding(value, [v], node2) nodes.append(node2) if nodes: return self.ctx.join_cfg_nodes(nodes), result return node, attr
def default_slot(self, node, default): # If the default is a method, call it and use its return type. fn = default.data[0] # TODO(mdemello): it is not clear what to use for self in fn_args; using # fn.cls.instantiate(node) is fraught because we are in the process of # constructing the class. If fn does not use `self` setting self=Any will # make no difference; if it does use `self` we might as well fall back to a # return type of `Any` rather than raising attribute errors in cases like # class A: # x = attr.ib(default=42) # y = attr.ib() # @y.default # def _y(self): # return self.x # # The correct thing to do would probably be to defer inference if we see a # default method, then infer all the method-based defaults after the class # is fully constructed. The workaround is simply to use type annotations, # which users should ideally be doing anyway. self_var = self.ctx.new_unsolvable(node) fn_args = function.Args(posargs=(self_var,)) node, default_var = fn.call(node, default.bindings[0], fn_args) self.default = default_var # If we don't have a type, set the type from the default type if not self.type_source: self.typ = get_type_from_default(default_var, self.ctx) self.type_source = TypeSource.DEFAULT # Return the original decorated method so we don't lose it. return node, default
def make_class(self, node, bases, f_locals): # If BuildClass.call() hits max depth, f_locals will be [unsolvable] # Since we don't support defining NamedTuple subclasses in a nested scope # anyway, we can just return unsolvable here to prevent a crash, and let the # invalid namedtuple error get raised later. if isinstance(f_locals.data[0], abstract.Unsolvable): return node, self.ctx.new_unsolvable(node) f_locals = abstract_utils.get_atomic_python_constant(f_locals) # retrieve __qualname__ to get the name of class name = f_locals["__qualname__"] nameval = abstract_utils.get_atomic_python_constant(name) if "." in nameval: nameval = nameval.rsplit(".", 1)[-1] name = self.ctx.convert.constant_to_var(nameval) # assemble the arguments that are compatible with NamedTupleFuncBuilder.call field_list = [] defaults = [] cls_locals = classgen.get_class_locals( nameval, allow_methods=True, ordering=classgen.Ordering.FIRST_ANNOTATE, ctx=self.ctx) for k, local in cls_locals.items(): assert local.typ if k in f_locals: defaults.append(f_locals[k]) k = self.ctx.convert.constant_to_var(k, node=node) field_list.append(self.ctx.convert.build_tuple(node, (k, local.typ))) anno = self.ctx.convert.build_list(node, field_list) posargs = (name, anno) args = function.Args(posargs=posargs) node, cls_var = self.namedtuple.call(node, None, args, bases) cls_val = abstract_utils.get_atomic_value(cls_var) if not isinstance(cls_val, abstract.Unsolvable): # set __new__.__defaults__ defaults = self.ctx.convert.build_tuple(node, defaults) node, new_attr = self.ctx.attribute_handler.get_attribute( node, cls_val, "__new__") new_attr = abstract_utils.get_atomic_value(new_attr) node = self.ctx.attribute_handler.set_attribute(node, new_attr, "__defaults__", defaults) # set the attribute without overriding special namedtuple attributes node, fields = self.ctx.attribute_handler.get_attribute( node, cls_val, "_fields") fields = abstract_utils.get_atomic_python_constant(fields, tuple) fields = [abstract_utils.get_atomic_python_constant(field, str) for field in fields] for key in f_locals: if key in self._prohibited: self.ctx.errorlog.not_writable(self.ctx.vm.frames, cls_val, key) if key not in abstract_utils.CLASS_LEVEL_IGNORE and key not in fields: node = self.ctx.attribute_handler.set_attribute( node, cls_val, key, f_locals[key]) return node, cls_var
def test_call_with_duplicate_keyword(self): f = self._simple_sig([self._ctx.convert.int_type] * 2) args = function.Args( posargs=(self._ctx.convert.build_int(self._ctx.root_node), self._ctx.convert.build_int(self._ctx.root_node)), namedargs={"_1": self._ctx.convert.build_int(self._ctx.root_node)}) self.assertRaises(function.DuplicateKeyword, f.call, self._ctx.root_node, f, args)
def test_change_defaults(self): f = self._make_func( param_names=("a", "b", "c"), defaults=(self._ctx.convert.build_int(self._ctx.root_node),)) args = function.Args( posargs=(self._ctx.convert.build_int(self._ctx.root_node), self._ctx.convert.build_int(self._ctx.root_node))) f.call(self._ctx.root_node, f, args) new_defaults = self._ctx.convert.build_tuple( self._ctx.root_node, (self._ctx.convert.build_int(self._ctx.root_node), self._ctx.convert.build_int(self._ctx.root_node))) f.set_function_defaults(self._ctx.root_node, new_defaults) f.call(self._ctx.root_node, f, args) args = function.Args( posargs=(self._ctx.convert.build_int(self._ctx.root_node),)) f.call(self._ctx.root_node, f, args)
def test_call_wrong_argcount(self): self._ctx.vm.push_frame(frame_state.SimpleFrame()) node, result = self._is_instance.call( self._node, None, function.Args((), self.new_dict(), None, None)) self.assertEqual(self._node, node) self.assertIsInstance(abstract_utils.get_atomic_value(result), abstract.Unsolvable) self.assertRegex(str(self._ctx.errorlog), "missing-parameter")
def call_pytd(self, node, name, *args): """Call the (original) pytd version of a method we overwrote.""" return function.call_function( self.ctx, node, self._super[name], function.Args(args), fallback_to_unsolvable=False)
def test_simple_call(self): f = self._simple_sig([self._ctx.convert.str_type], ret_type=self._ctx.convert.int_type) args = function.Args( (self._ctx.convert.build_string(self._ctx.root_node, "hello"),)) node, ret = f.call(self._ctx.root_node, f, args) self.assertIs(node, self._ctx.root_node) ret_val, = ret.data self.assertEqual(ret_val.cls, self._ctx.convert.int_type)
def test_call_empty_type_parameter_instance(self): instance = abstract.Instance(self._ctx.convert.list_type, self._ctx) t = abstract.TypeParameter(abstract_utils.T, self._ctx) t_instance = abstract.TypeParameterInstance(t, instance, self._ctx) node, ret = t_instance.call(self._node, t_instance.to_binding(self._node), function.Args(posargs=())) self.assertIs(node, self._node) retval, = ret.data self.assertIs(retval, self._ctx.convert.empty)
def call(self, node, _, args): self.match_args(node, args) arg = args.posargs[0] node, fn = self.get_underlying_method(node, arg, "__abs__") if fn is not None: return function.call_function(self.ctx, node, fn, function.Args( ())) else: return node, self.ctx.new_unsolvable(node)
def test_call_with_bad_varargs(self): f = self._make_func( varargs_name="arg", annotations={"arg": self._ctx.convert.str_type}) starargs = self._ctx.convert.build_tuple( self._ctx.root_node, (self._ctx.convert.build_string(self._ctx.root_node, ""), self._ctx.convert.build_int(self._ctx.root_node))) args = function.Args(posargs=(), starargs=starargs) self.assertRaises(function.WrongArgTypes, f.call, self._ctx.root_node, f, args)
def test_call_with_multiple_varargs_bindings(self): f = self._make_func( varargs_name="arg", annotations={"arg": self._ctx.convert.str_type}) arg = self._ctx.program.NewVariable() arg.AddBinding(self._ctx.convert.primitive_class_instances[str], [], self._ctx.root_node) arg.AddBinding(self._ctx.convert.primitive_class_instances[int], [], self._ctx.root_node) starargs = self._ctx.convert.build_tuple(self._ctx.root_node, (arg,)) args = function.Args(posargs=(), starargs=starargs) f.call(self._ctx.root_node, f, args)
def test_call_wrong_keywords(self): self._ctx.vm.push_frame(frame_state.SimpleFrame()) x = self.new_var(abstract.Unknown(self._ctx)) node, result = self._is_instance.call( self._node, None, function.Args( (x, x), self.new_dict(foo=x), None, None)) self.assertEqual(self._node, node) self.assertIsInstance(abstract_utils.get_atomic_value(result), abstract.Unsolvable) self.assertRegex( str(self._ctx.errorlog), r"foo.*isinstance.*\[wrong-keyword-args\]")
def test_call_with_multiple_arg_bindings(self): f = self._simple_sig([self._ctx.convert.str_type]) arg = self._ctx.program.NewVariable() arg.AddBinding(self._ctx.convert.primitive_class_instances[str], [], self._ctx.root_node) arg.AddBinding(self._ctx.convert.primitive_class_instances[int], [], self._ctx.root_node) args = function.Args((arg,)) node, ret = f.call(self._ctx.root_node, f, args) self.assertIs(node, self._ctx.root_node) self.assertIs(ret.data[0], self._ctx.convert.none)
def test_call_with_bad_kwargs(self): f = self._make_func( kwargs_name="kwarg", annotations={"kwarg": self._ctx.convert.str_type}) kwargs = abstract.Dict(self._ctx) kwargs.update(self._ctx.root_node, {"_1": self._ctx.convert.build_int(self._ctx.root_node)}) kwargs = kwargs.to_variable(self._ctx.root_node) args = function.Args( posargs=(), namedargs=abstract.Dict(self._ctx), starstarargs=kwargs) self.assertRaises(function.WrongArgTypes, f.call, self._ctx.root_node, f, args)
def call(self, node, _, args): self.match_args(node, args) arg, default = self._get_args(args) node, fn = self.get_underlying_method(node, arg, "__next__") if fn is not None: node, ret = function.call_function(self.ctx, node, fn, function.Args(())) ret.PasteVariable(default) return node, ret else: return node, self.ctx.new_unsolvable(node)
def test_call_type_parameter_instance(self): instance = abstract.Instance(self._ctx.convert.list_type, self._ctx) instance.merge_instance_type_parameter( self._ctx.root_node, abstract_utils.T, self._ctx.convert.int_type.to_variable(self._ctx.root_node)) t = abstract.TypeParameter(abstract_utils.T, self._ctx) t_instance = abstract.TypeParameterInstance(t, instance, self._ctx) node, ret = t_instance.call(self._node, t_instance.to_binding(self._node), function.Args(posargs=())) self.assertIs(node, self._node) retval, = ret.data self.assertEqual(retval.cls, self._ctx.convert.int_type)
def test_call_with_bad_default(self): f = self._make_func( param_names=("a", "b"), defaults=(self._ctx.convert.build_string(self._ctx.root_node, ""),), annotations={ "a": self._ctx.convert.int_type, "b": self._ctx.convert.str_type }) args = function.Args( posargs=(self._ctx.convert.build_int(self._ctx.root_node), self._ctx.convert.build_int(self._ctx.root_node))) self.assertRaises(function.WrongArgTypes, f.call, self._ctx.root_node, f, args)
def test_call_with_defaults(self): f = self._make_func( param_names=("a", "b", "c"), defaults=(self._ctx.convert.build_int(self._ctx.root_node),), annotations={ "a": self._ctx.convert.int_type, "b": self._ctx.convert.int_type, "c": self._ctx.convert.int_type }) args = function.Args( posargs=(self._ctx.convert.build_int(self._ctx.root_node), self._ctx.convert.build_int(self._ctx.root_node))) f.call(self._ctx.root_node, f, args) args = function.Args( posargs=(self._ctx.convert.build_int(self._ctx.root_node), self._ctx.convert.build_int(self._ctx.root_node), self._ctx.convert.build_int(self._ctx.root_node))) f.call(self._ctx.root_node, f, args) args = function.Args( posargs=(self._ctx.convert.build_int(self._ctx.root_node),)) self.assertRaises(function.MissingParameter, f.call, self._ctx.root_node, f, args)
def test_call_with_kwargs(self): f = self._make_func( kwargs_name="kwarg", annotations={"kwarg": self._ctx.convert.str_type}) kwargs = abstract.Dict(self._ctx) kwargs.update( self._ctx.root_node, { "_1": self._ctx.convert.build_string(self._ctx.root_node, "1"), "_2": self._ctx.convert.build_string(self._ctx.root_node, "2") }) kwargs = kwargs.to_variable(self._ctx.root_node) args = function.Args( posargs=(), namedargs=abstract.Dict(self._ctx), starstarargs=kwargs) f.call(self._ctx.root_node, f, args)
def test_call_type_parameter_instance_with_wrong_args(self): instance = abstract.Instance(self._ctx.convert.list_type, self._ctx) instance.merge_instance_type_parameter( self._ctx.root_node, abstract_utils.T, self._ctx.convert.int_type.to_variable(self._ctx.root_node)) t = abstract.TypeParameter(abstract_utils.T, self._ctx) t_instance = abstract.TypeParameterInstance(t, instance, self._ctx) posargs = (self._ctx.new_unsolvable(self._node),) * 3 node, ret = t_instance.call(self._node, t_instance.to_binding(self._node), function.Args(posargs=posargs)) self.assertIs(node, self._node) self.assertTrue(ret.bindings) error, = self._ctx.errorlog self.assertEqual(error.name, "wrong-arg-count")
def test_call_with_varargs(self): f = self._make_func( varargs_name="arg", annotations={ "arg": self._ctx.convert.str_type, "return": self._ctx.convert.str_type }) starargs = self._ctx.convert.build_tuple( self._ctx.root_node, (self._ctx.convert.build_string(self._ctx.root_node, ""),)) args = function.Args(posargs=(), starargs=starargs) node, ret = f.call(self._ctx.root_node, f, args) self.assertIs(node, self._ctx.root_node) self.assertIs(ret.data[0].cls, self._ctx.convert.str_type)
def call_slot(self, node, *args, **kwargs): """Implementation of CallableClass.__call__.""" if kwargs: raise function.WrongKeywordArgs( function.Signature.from_callable(self), function.Args(posargs=args, namedargs=kwargs), self.ctx, kwargs.keys()) if len(args) != self.num_args: raise function.WrongArgCount( function.Signature.from_callable(self), function.Args(posargs=args), self.ctx) formal_args = [(function.argname(i), self.formal_type_parameters[i]) for i in range(self.num_args)] substs = [datatypes.AliasingDict()] bad_param = None for view in abstract_utils.get_views(args, node): arg_dict = { function.argname(i): view[args[i]] for i in range(self.num_args) } subst, bad_param = self.ctx.matcher(node).compute_subst( formal_args, arg_dict, view, None) if subst is not None: substs = [subst] break else: if bad_param: raise function.WrongArgTypes( function.Signature.from_callable(self), function.Args(posargs=args), self.ctx, bad_param=bad_param) ret = self.ctx.annotation_utils.sub_one_annotation( node, self.formal_type_parameters[abstract_utils.RET], substs) node, retvar = self.ctx.vm.init_class(node, ret) return node, retvar
def _call_converter_function(self, node, converter_var, args): """Run converter and return the input and return types.""" binding = converter_var.bindings[0] fn = binding.data sig = self._get_converter_sig(fn, args) if sig.param_names and sig.param_names[0] in sig.annotations: input_type = sig.annotations[sig.param_names[0]] else: input_type = self.ctx.convert.unsolvable if sig.has_return_annotation: return_type = sig.annotations["return"] else: fn_args = function.Args(posargs=(input_type.instantiate(node),)) node, ret_var = fn.call(node, binding, fn_args) return_type = self.ctx.convert.merge_classes(ret_var.data) return input_type, return_type
def _get_default_var(self, node, args): if "default" in args.namedargs and "default_factory" in args.namedargs: # The pyi signatures should prevent this; check left in for safety. raise function.DuplicateKeyword(self.signatures[0].signature, args, self.ctx, "default") elif "default" in args.namedargs: default_var = args.namedargs["default"] elif "default_factory" in args.namedargs: factory_var = args.namedargs["default_factory"] factory, = factory_var.data f_args = function.Args(posargs=()) node, default_var = factory.call(node, factory_var.bindings[0], f_args) else: default_var = None return node, default_var
def _get_default_var(self, node, args): if "default" in args.namedargs and "factory" in args.namedargs: # attr.ib(factory=x) is syntactic sugar for attr.ib(default=Factory(x)). raise function.DuplicateKeyword(self.sig, args, self.ctx, "default") elif "default" in args.namedargs: default_var = args.namedargs["default"] elif "factory" in args.namedargs: mod = self.ctx.vm.import_module("attr", "attr", 0) node, attr = self.ctx.attribute_handler.get_attribute( node, mod, "Factory") # We know there is only one value because Factory is in the overlay. factory, = attr.data factory_args = function.Args(posargs=(args.namedargs["factory"],)) node, default_var = factory.call(node, attr.bindings[0], factory_args) else: default_var = None return node, default_var