def init_subclass(self, node, cls): # Subclasses of Module call self.setup() when creating instances. cls.additional_init_methods.append("setup") dc = ModuleDataclass.make(self.vm) cls_var = cls.to_variable(node) args = function.Args(posargs=(cls_var,), namedargs=abstract.Dict(self.vm)) node, _ = dc.call(node, None, args) return node
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 self.vm.call_function(node, fn, function.Args(())) else: return node, self.vm.new_unsolvable(node)
def test_call_with_duplicate_keyword(self): f = self._simple_sig([self._vm.convert.int_type]*2) args = function.Args( posargs=(self._vm.convert.build_int(self._vm.root_node), self._vm.convert.build_int(self._vm.root_node)), namedargs={"_1": self._vm.convert.build_int(self._vm.root_node)}) self.assertRaises(function.DuplicateKeyword, f.call, self._vm.root_node, f, args)
def test_call_wrong_argcount(self): self._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.assertRegexpMatches(str(self._vm.errorlog), "missing-parameter")
def test_simple_call(self): f = self._simple_sig([self._vm.convert.str_type], ret_type=self._vm.convert.int_type) args = function.Args( (self._vm.convert.build_string(self._vm.root_cfg_node, "hello"),)) node, ret = f.call(self._vm.root_cfg_node, f, args) self.assertIs(node, self._vm.root_cfg_node) ret_val, = ret.data self.assertEqual(ret_val.cls, self._vm.convert.int_type)
def test_call_empty_type_parameter_instance(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm) t = abstract.TypeParameter(abstract_utils.T, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) 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._vm.convert.empty)
def call(self, node, _, args): self.match_args(node, args) arg, default = self._get_args(args) node, fn = self.get_underlying_method(node, arg, self.vm.convert.next_attr) if fn is not None: node, ret = self.vm.call_function(node, fn, function.Args(())) ret.PasteVariable(default) return node, ret else: return node, self.vm.new_unsolvable(node)
def test_call_with_bad_varargs(self): f = self._make_func( varargs_name="arg", annotations={"arg": self._vm.convert.str_type}) starargs = abstract.Tuple( (self._vm.convert.build_string(self._vm.root_node, ""), self._vm.convert.build_int(self._vm.root_node)), self._vm).to_variable(self._vm.root_node) args = function.Args(posargs=(), starargs=starargs) self.assertRaises(function.WrongArgTypes, f.call, self._vm.root_node, f, args)
def test_call_wrong_keywords(self): self._vm.push_frame(frame_state.SimpleFrame()) x = self.new_var(abstract.Unknown(self._vm)) 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) six.assertRegex(self, str(self._vm.errorlog), r"foo.*isinstance.*\[wrong-keyword-args\]")
def make_class(self, node, 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 f_locals.data[0].isinstance_Unsolvable(): return node, self.vm.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__"] # assemble the arguments that are compatible with NamedTupleFuncBuilder.call field_list = [] defaults = [] cls_locals = classgen.get_class_locals( abstract_utils.get_atomic_python_constant(name), allow_methods=True, ordering=classgen.Ordering.FIRST_ANNOTATE, vm=self.vm) for k, local in cls_locals.items(): assert local.typ if k in f_locals: defaults.append(f_locals[k]) k = self.vm.convert.constant_to_var(k, node=node) field_list.append(self.vm.convert.build_tuple(node, (k, local.typ))) anno = self.vm.convert.build_list(node, field_list) posargs = (name, anno) args = function.Args(posargs=posargs) node, cls_var = self.namedtuple.call(node, None, args) cls_val = abstract_utils.get_atomic_value(cls_var) if not isinstance(cls_val, abstract.Unsolvable): # set __new__.__defaults__ defaults = abstract.Tuple(tuple(defaults), self.vm).to_variable(node) node, new_attr = self.vm.attribute_handler.get_attribute( node, cls_val, "__new__") new_attr = abstract_utils.get_atomic_value(new_attr) node = self.vm.attribute_handler.set_attribute( node, new_attr, "__defaults__", defaults) # set the attribute without overriding special namedtuple attributes node, fields = self.vm.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.vm.errorlog.not_writable(self.vm.frames, cls_val, key) if key not in self._special and key not in fields: node = self.vm.attribute_handler.set_attribute( node, cls_val, key, f_locals[key]) return node, cls_var
def test_change_defaults(self): f = self._make_func( param_names=("a", "b", "c"), defaults=(self._vm.convert.build_int(self._vm.root_cfg_node),) ) args = function.Args( posargs=(self._vm.convert.build_int(self._vm.root_cfg_node), self._vm.convert.build_int(self._vm.root_cfg_node)) ) f.call(self._vm.root_cfg_node, f, args) new_defaults = abstract.Tuple( (self._vm.convert.build_int(self._vm.root_cfg_node), self._vm.convert.build_int(self._vm.root_cfg_node)), self._vm).to_variable(self._vm.root_cfg_node) f.set_function_defaults(self._vm.root_cfg_node, new_defaults) f.call(self._vm.root_cfg_node, f, args) args = function.Args( posargs=(self._vm.convert.build_int(self._vm.root_cfg_node),) ) f.call(self._vm.root_cfg_node, f, args)
def test_call_with_multiple_arg_bindings(self): f = self._simple_sig([self._vm.convert.str_type]) arg = self._vm.program.NewVariable() arg.AddBinding(self._vm.convert.primitive_class_instances[str], [], self._vm.root_cfg_node) arg.AddBinding(self._vm.convert.primitive_class_instances[int], [], self._vm.root_cfg_node) args = function.Args((arg,)) node, ret = f.call(self._vm.root_cfg_node, f, args) self.assertIs(node, self._vm.root_cfg_node) self.assertIs(ret.data[0], self._vm.convert.none)
def test_call_type_parameter_instance(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm) instance.merge_instance_type_parameter( self._vm.root_cfg_node, abstract_utils.T, self._vm.convert.int_type.to_variable(self._vm.root_cfg_node)) t = abstract.TypeParameter(abstract_utils.T, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) 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._vm.convert.int_type)
def _get_attribute_computed(self, node, cls, name, valself, compute_function): """Call compute_function (if defined) to compute an attribute.""" assert isinstance(cls, (mixin.Class, abstract.AMBIGUOUS_OR_EMPTY)), cls if (valself and not isinstance(valself.data, abstract.Module) and self._computable(name)): attr_var = self._lookup_from_mro(node, cls, compute_function, valself, skip=self.vm.convert.object_type) if attr_var and attr_var.bindings: name_var = abstract.AbstractOrConcreteValue( name, self.vm.convert.str_type, self.vm).to_variable(node) return self.vm.call_function(node, attr_var, function.Args((name_var,))) return node, None
def make_class(self, node, f_locals): f_locals = abstract_utils.get_atomic_python_constant(f_locals) # retrieve __qualname__ to get the name of class name = f_locals["__qualname__"] # retrieve __annotations__ to get the dict # with key-value pair of (variable, type) anno = f_locals.get("__annotations__", {}) if anno: anno = abstract_utils.get_atomic_value(anno) # assemble the arguments that are compatible with NamedTupleFuncBuilder.call field_list = [] defaults = [] for k, v in anno.items(): if k in f_locals: defaults.append(f_locals.get(k)) # TODO(ahxun): check if the value matches the declared type k = self.vm.convert.constant_to_var(k, node=node) field_list.append(self.vm.convert.build_tuple(node, (k, v))) anno = self.vm.convert.build_list(node, field_list) posargs = (name, anno) args = function.Args(posargs=posargs) node, cls_var = self.namedtuple.call(node, None, args) cls_val = abstract_utils.get_atomic_value(cls_var) if not isinstance(cls_val, abstract.Unsolvable): # set __new__.__defaults__ defaults = abstract.Tuple(tuple(defaults), self.vm).to_variable(node) node, new_attr = self.vm.attribute_handler.get_attribute( node, cls_val, "__new__") new_attr = abstract_utils.get_atomic_value(new_attr) node = self.vm.attribute_handler.set_attribute( node, new_attr, "__defaults__", defaults) # set the attribute without overriding special namedtuple attributes node, fields = self.vm.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.vm.errorlog.not_writable(self.vm.frames, cls_val, key) if key not in self._special and key not in fields: node = self.vm.attribute_handler.set_attribute( node, cls_val, key, f_locals[key]) return node, cls_var
def test_call_with_kwonly_args(self): f = self._make_func( param_names=("test",), kwonly_params=("a", "b"), annotations={ "test": self._vm.convert.str_type, "a": self._vm.convert.str_type, "b": self._vm.convert.str_type } ) kwargs = abstract.Dict(self._vm) kwargs.update( self._vm.root_cfg_node, { "a": self._vm.convert.build_string(self._vm.root_cfg_node, "2"), "b": self._vm.convert.build_string(self._vm.root_cfg_node, "3") } ) kwargs = kwargs.to_variable(self._vm.root_cfg_node) args = function.Args( posargs=(self._vm.convert.build_string(self._vm.root_cfg_node, "1"),), namedargs=abstract.Dict(self._vm), starstarargs=kwargs ) f.call(self._vm.root_cfg_node, f, args) kwargs = abstract.Dict(self._vm) kwargs.update( self._vm.root_cfg_node, {"b": self._vm.convert.build_string(self._vm.root_cfg_node, "3")} ) kwargs = kwargs.to_variable(self._vm.root_cfg_node) args = function.Args( posargs=(self._vm.convert.build_string(self._vm.root_cfg_node, "1"), self._vm.convert.build_int(self._vm.root_cfg_node)), namedargs=abstract.Dict(self._vm), starstarargs=kwargs ) self.assertRaises(function.MissingParameter, f.call, self._vm.root_cfg_node, f, args)
def test_call_with_multiple_varargs_bindings(self): f = self._make_func( varargs_name="arg", annotations={"arg": self._vm.convert.str_type}) arg = self._vm.program.NewVariable() arg.AddBinding(self._vm.convert.primitive_class_instances[str], [], self._vm.root_cfg_node) arg.AddBinding(self._vm.convert.primitive_class_instances[int], [], self._vm.root_cfg_node) starargs = abstract.Tuple((arg,), self._vm) starargs = starargs.to_variable(self._vm.root_cfg_node) args = function.Args(posargs=(), starargs=starargs) f.call(self._vm.root_cfg_node, f, args)
def test_call_with_varargs(self): f = self._make_func( varargs_name="arg", annotations={"arg": self._vm.convert.str_type, "return": self._vm.convert.str_type} ) starargs = abstract.Tuple( (self._vm.convert.build_string(self._vm.root_cfg_node, ""),), self._vm).to_variable(self._vm.root_cfg_node) args = function.Args(posargs=(), starargs=starargs) node, ret = f.call(self._vm.root_cfg_node, f, args) self.assertIs(node, self._vm.root_cfg_node) self.assertIs(ret.data[0].cls, self._vm.convert.str_type)
def test_call_with_bad_default(self): f = self._make_func( param_names=("a", "b"), defaults=(self._vm.convert.build_string(self._vm.root_node, ""),), annotations={ "a": self._vm.convert.int_type, "b": self._vm.convert.str_type }) args = function.Args( posargs=(self._vm.convert.build_int(self._vm.root_node), self._vm.convert.build_int(self._vm.root_node))) self.assertRaises(function.WrongArgTypes, f.call, self._vm.root_node, f, args)
def test_call_with_defaults(self): f = self._make_func( param_names=("a", "b", "c"), defaults=(self._vm.convert.build_int(self._vm.root_node),), annotations={ "a": self._vm.convert.int_type, "b": self._vm.convert.int_type, "c": self._vm.convert.int_type }) args = function.Args( posargs=(self._vm.convert.build_int(self._vm.root_node), self._vm.convert.build_int(self._vm.root_node))) f.call(self._vm.root_node, f, args) args = function.Args( posargs=(self._vm.convert.build_int(self._vm.root_node), self._vm.convert.build_int(self._vm.root_node), self._vm.convert.build_int(self._vm.root_node))) f.call(self._vm.root_node, f, args) args = function.Args( posargs=(self._vm.convert.build_int(self._vm.root_node),)) self.assertRaises(function.MissingParameter, f.call, self._vm.root_node, f, args)
def test_call_type_parameter_instance_with_wrong_args(self): instance = abstract.Instance(self._vm.convert.list_type, self._vm) instance.merge_instance_type_parameter( self._vm.root_cfg_node, abstract_utils.T, self._vm.convert.int_type.to_variable(self._vm.root_cfg_node)) t = abstract.TypeParameter(abstract_utils.T, self._vm) t_instance = abstract.TypeParameterInstance(t, instance, self._vm) posargs = (self._vm.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._vm.errorlog self.assertEqual(error.name, "wrong-arg-count")
def test_call_with_bad_kwargs(self): f = self._make_func( kwargs_name="kwarg", annotations={"kwarg": self._vm.convert.str_type}) kwargs = abstract.Dict(self._vm) kwargs.update(self._vm.root_cfg_node, {"_1": self._vm.convert.build_int(self._vm.root_cfg_node)}) kwargs = kwargs.to_variable(self._vm.root_cfg_node) args = function.Args( posargs=(), namedargs=abstract.Dict(self._vm), starstarargs=kwargs ) self.assertRaises(function.WrongArgTypes, f.call, self._vm.root_cfg_node, f, args)
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.vm, "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 test_call_with_kwargs(self): f = self._make_func( kwargs_name="kwarg", annotations={"kwarg": self._vm.convert.str_type}) kwargs = abstract.Dict(self._vm) kwargs.update( self._vm.root_node, { "_1": self._vm.convert.build_string(self._vm.root_node, "1"), "_2": self._vm.convert.build_string(self._vm.root_node, "2") }) kwargs = kwargs.to_variable(self._vm.root_node) args = function.Args( posargs=(), namedargs=abstract.Dict(self._vm), starstarargs=kwargs ) f.call(self._vm.root_node, f, args)
def call(self, node, func, args): self.match_args(node, args) if len(args.posargs) != 2: return super().call(node, func, args) pred, seq = args.posargs # Special case filter(None, seq). We remove None from seq and then call the # regular filter() so we don't need to reimplement eveything. if pred.data == [self.vm.convert.none]: result = self.vm.program.NewVariable() for b in seq.bindings: ret = self._filter_none(b.data, node) if ret: result.PasteVariable(ret, node, {b}) else: result.PasteBinding(b) args = function.Args((pred, result)) return super().call(node, func, args)
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.signatures[0].signature, args, self.vm, "default") elif "default" in args.namedargs: default_var = args.namedargs["default"] elif "factory" in args.namedargs: mod = self.vm.import_module("attr", "attr", 0) node, attr = self.vm.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
def call(self, node, _, args): posargs = args.posargs namedargs = self.vm.convert.value_to_constant(args.namedargs, dict) if namedargs and self.vm.python_version < (3, 6): errmsg = "Keyword syntax for NamedTuple is only supported in Python 3.6+" self.vm.errorlog.invalid_namedtuple_arg(self.vm.frames, err_msg=errmsg) if namedargs and len(posargs) == 1: namedargs = [abstract.Tuple( (self.vm.convert.build_string(node, k), v), self.vm).to_variable(node) for k, v in namedargs.items()] namedargs = abstract.List(namedargs, self.vm).to_variable(node) posargs += (namedargs,) args = function.Args(posargs) elif namedargs: errmsg = ("Either list of fields or keywords can be provided to " "NamedTuple, not both") self.vm.errorlog.invalid_namedtuple_arg(self.vm.frames, err_msg=errmsg) return self.namedtuple.call(node, None, args)
def call_metaclass_init(self, node): """Call the metaclass's __init__ method if it does anything interesting.""" if not self.cls: return node node, init = self.vm.attribute_handler.get_attribute( node, self.cls, "__init__") if not init or not any( f.isinstance_InterpreterFunction() for f in init.data): # Only an InterpreterFunction has interesting side effects. return node # TODO(rechen): The signature is (cls, name, bases, dict); should we fill in # the last three args more precisely? args = function.Args(posargs=(self.to_variable(node),) + tuple( self.vm.new_unsolvable(node) for _ in range(3))) log.debug("Calling __init__ on metaclass %s of class %s", self.cls.name, self.name) node, _ = self.vm.call_function(node, init, args) return node
def create_method_arguments(self, node, method, use_defaults=False): """Create arguments for the given method. Creates Unknown objects as arguments for the given method. Note that we don't need to take parameter annotations into account as InterpreterFunction.call() will take care of that. Args: node: The current node. method: An abstract.InterpreterFunction. use_defaults: Whether to use parameter defaults for arguments. When True, unknown arguments are created with force=False, as it is fine to use Unsolvable rather than Unknown objects for type-checking defaults. Returns: A tuple of a node and a function.Args object. """ args = [] num_posargs = method.argcount(node) num_posargs_no_default = num_posargs - len(method.defaults) for i in range(num_posargs): default_idx = i - num_posargs_no_default if use_defaults and default_idx >= 0: arg = method.defaults[default_idx] else: arg = self.convert.create_new_unknown(node, force=not use_defaults) args.append(arg) kws = {} for key in method.signature.kwonly_params: if use_defaults and key in method.kw_defaults: kws[key] = method.kw_defaults[key] else: kws[key] = self.convert.create_new_unknown( node, force=not use_defaults) starargs = self.create_varargs(node) if method.has_varargs() else None starstarargs = self.create_kwargs( node) if method.has_kwargs() else None return node, function.Args(posargs=tuple(args), namedargs=kws, starargs=starargs, starstarargs=starstarargs)
def test_call_with_type_parameter(self): ret_cls = abstract.ParameterizedClass( self._vm.convert.list_type, {abstract_utils.T: abstract.TypeParameter(abstract_utils.T, self._vm)}, self._vm ) f = self._make_func( param_names=("test",), annotations={ "test": abstract.TypeParameter(abstract_utils.T, self._vm), "return": ret_cls } ) args = function.Args( posargs=(self._vm.convert.build_int(self._vm.root_cfg_node),)) _, ret = f.call(self._vm.root_cfg_node, f, args) # ret is an Instance(ParameterizedClass(list, {abstract_utils.T: int})) # but we really only care about T. self.assertIs(ret.data[0].cls.formal_type_parameters[abstract_utils.T], self._vm.convert.int_type)