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_node, { "a": self._vm.convert.build_string(self._vm.root_node, "2"), "b": self._vm.convert.build_string(self._vm.root_node, "3") }) kwargs = kwargs.to_variable(self._vm.root_node) args = function.Args( posargs=(self._vm.convert.build_string(self._vm.root_node, "1"),), namedargs=abstract.Dict(self._vm), starstarargs=kwargs) f.call(self._vm.root_node, f, args) kwargs = abstract.Dict(self._vm) kwargs.update(self._vm.root_node, {"b": self._vm.convert.build_string(self._vm.root_node, "3")}) kwargs = kwargs.to_variable(self._vm.root_node) args = function.Args( posargs=(self._vm.convert.build_string(self._vm.root_node, "1"), self._vm.convert.build_int(self._vm.root_node)), namedargs=abstract.Dict(self._vm), starstarargs=kwargs) self.assertRaises(function.MissingParameter, f.call, self._vm.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._vm.convert.build_int(self._vm.root_cfg_node),), annotations={ "a": self._vm.convert.str_type, "b": self._vm.convert.int_type, "c": self._vm.convert.int_type, "arg": self._vm.convert.primitive_classes[float], "kwarg": self._vm.convert.primitive_classes[bool] } ) posargs = (self._vm.convert.build_string(self._vm.root_cfg_node, "1"), self._vm.convert.build_int(self._vm.root_cfg_node)) float_inst = self._vm.convert.primitive_class_instances[float] stararg = abstract.Tuple((float_inst.to_variable(self._vm.root_cfg_node),), self._vm).to_variable(self._vm.root_cfg_node) namedargs = abstract.Dict(self._vm) kwarg = abstract.Dict(self._vm) kwarg.update(self._vm.root_cfg_node, {"x": self._vm.convert.build_bool(self._vm.root_cfg_node), "y": self._vm.convert.build_bool(self._vm.root_cfg_node)}) kwarg = kwarg.to_variable(self._vm.root_cfg_node) args = function.Args(posargs, namedargs, stararg, kwarg) f.call(self._vm.root_cfg_node, f, args)
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 call(self, node, func, args): args = args.simplify(node) self.match_args(node, args, match_all_views=True) # As long as the types match we do not really care about the actual # class name. But, if we have a string literal value as the name arg, # we will use it. name_arg = args.namedargs.get(self._name_arg_name) or args.posargs[0] try: _ = abstract_utils.get_atomic_python_constant(name_arg, str) except abstract_utils.ConversionError: name_arg = self.vm.convert.constant_to_var( "_NewType_Internal_Class_Name_%d_" % self.internal_name_counter) type_arg = args.namedargs.get(self._type_arg_name) or args.posargs[1] try: type_value = abstract_utils.get_atomic_value(type_arg) except abstract_utils.ConversionError: # We need the type arg to be an atomic value. If not, we just # silently return unsolvable. return node, self.vm.new_unsolvable(node) value_arg_name = "val" constructor = overlay_utils.make_method( self.vm, node, name="__init__", params=[Param(value_arg_name, type_value)]) members = abstract.Dict(self.vm) members.set_str_item(node, "__init__", constructor) return self.vm.make_class(node, name_arg, (type_arg, ), members.to_variable(node), None)
def test_compatible_with__after_unambiguous_update(self): unambiguous_dict = abstract.Dict(self._vm) unambiguous_dict.set_str_item(self._node, "a", self._vm.new_unsolvable(self._node)) self._d.update(self._node, unambiguous_dict) self.assertIs(True, compare.compatible_with(self._d, True)) self.assertIs(False, compare.compatible_with(self._d, False))
def test_compatible_with__after_ambiguous_update(self): ambiguous_dict = abstract.Dict(self._vm) ambiguous_dict.merge_instance_type_parameter( self._node, abstract_utils.K, self._vm.new_unsolvable(self._node)) ambiguous_dict.could_contain_anything = True self._d.update(self._node, ambiguous_dict) self.assertAmbiguous(self._d)
def call(self, node, func, args): args = args.simplify(node) self._match_args(node, args, match_all_views=True) # As long as the types match we do not really care about the actual # class name. But, if we have a string literal value as the name arg, # we will use it. name_arg = args.namedargs.get(self._name_arg_name) or args.posargs[0] try: _ = abstract.get_atomic_python_constant(name_arg, str) except abstract.ConversionError: name_arg = self.vm.convert.constant_to_var( "_NewType_Internal_Class_Name_%d_" % self.internal_name_counter) type_arg = args.namedargs.get(self._type_arg_name) or args.posargs[1] try: type_value = abstract.get_atomic_value(type_arg) except abstract.ConversionError: # We need the type arg to be an atomic value. If not, we just # silently return unsolvable. return node, self.vm.convert.create_new_unsolvable(node) value_arg_name = "val" constructor = abstract.SimpleFunction( name="__init__", param_names=("self", value_arg_name), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations={value_arg_name: type_value}, late_annotations={}, vm=self.vm, ).to_variable(node) members = abstract.Dict(self.vm) members.set_str_item(node, "__init__", constructor) return node, self.vm.make_class(node, name_arg, (type_arg,), members.to_variable(node), None)
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 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 test_compatible_with__after_ambiguous_update(self): ambiguous_dict = abstract.Dict(self._vm) ambiguous_dict.merge_type_parameter( self._node, abstract.K, self._vm.convert.create_new_unsolvable(self._node)) ambiguous_dict.could_contain_anything = True self._d.update(self._node, ambiguous_dict) self.assertIs(True, self._d.compatible_with(True)) self.assertIs(True, self._d.compatible_with(False))
def create_kwargs(self, node): key_type = self.primitive_class_instances[str].to_variable(node, "str") value_type = self.create_new_unknown(node, "kwargs_value") kwargs = abstract.Dict("kwargs", self) kwargs.overwrite_type_parameter( node, abstract.Dict.KEY_TYPE_PARAM, key_type) kwargs.overwrite_type_parameter( node, abstract.Dict.VALUE_TYPE_PARAM, value_type) return kwargs
def test_compatible_with__after_empty_update(self): empty_dict = abstract.Dict(self._vm) self._d.update(self._node, empty_dict) self.assertIs(False, self._d.compatible_with(True)) self.assertIs(True, self._d.compatible_with(False))
def build_map(self, node): """Create an empty VM dict.""" return abstract.Dict(self.vm).to_variable(node)
def setUp(self): super(DictTest, self).setUp() self._d = abstract.Dict(self._vm) self._var = self._program.NewVariable() self._var.AddBinding(abstract.Unknown(self._vm), [], self._node)
def setUp(self): super(DictTest, self).setUp() self._d = abstract.Dict("test_dict", self._vm, self._node) self._var = self._program.NewVariable("test_var") self._var.AddBinding(abstract.Unknown(self._vm))
def test_compatible_with__after_unambiguous_update(self): unambiguous_dict = abstract.Dict(self._vm) unambiguous_dict.set_str_item(self._node, "a", self._vm.new_unsolvable(self._node)) self._d.update(self._node, unambiguous_dict) self.assertTruthy(self._d)
def test_compatible_with__after_empty_update(self): empty_dict = abstract.Dict(self._vm) self._d.update(self._node, empty_dict) self.assertFalsy(self._d)
def call(self, node, func, args, alias_map=None): """Implements the behavior of the enum functional API.""" # Because of how this is called, we supply our own "self" argument. # See class_mixin.Class._call_new_and_init. args = args.simplify(node, self.vm) args = args.replace(posargs=(self.vm.new_unsolvable(node), ) + args.posargs) self.load_lazy_attribute("__new__") new = abstract_utils.get_atomic_value(self.members["__new__"]) # Note that super().call or _call_new_and_init won't work here, because # they don't raise FailedFunctionCall. new.match_args(node, args, alias_map) # There should only be 1 signature for Enum.__new__. assert len( new.signatures) == 1, "Expected only 1 Enum.__new__ signature." sig = new.signatures[0].signature argmap = {name: var for name, var, _ in sig.iter_args(args)} cls_name_var = argmap["value"] try: names = abstract_utils.get_atomic_python_constant(argmap["names"]) except abstract_utils.ConversionError as e: log.info( "Failed to unwrap values in enum functional interface:\n%s", e) return node, self.vm.new_unsolvable(node) if isinstance(names, str): names = names.replace(",", " ").split() fields = {name: self.vm.convert.build_int(node) for name in names} elif isinstance(names, dict): # Dict keys are strings, not strings in variables. The values are # variables, they don't need to be changed. fields = names else: # List of names, or list of (name, value) pairs. try: possible_pairs = [ abstract_utils.get_atomic_python_constant(p) for p in names ] except abstract_utils.ConversionError as e: log.debug("Failed to unwrap possible enum field pairs:\n %s", e) return node, self.vm.new_unsolvable(node) if not possible_pairs: fields = {} elif isinstance(possible_pairs[0], str): fields = { name: self.vm.convert.build_int(node) for name in possible_pairs } else: # List of (name_var, value_var) pairs. # The earlier get_atomic_python_constant call only unwrapped the tuple, # so the values in the tuple still need to be unwrapped. try: fields = { abstract_utils.get_atomic_python_constant(name): value for name, value in possible_pairs } except abstract_utils.ConversionError as e: log.debug("Failed to unwrap field names for enum:\n %s", e) return node, self.vm.new_unsolvable(node) cls_dict = abstract.Dict(self.vm) cls_dict.update(node, fields) metaclass = self.vm.loaded_overlays["enum"].members["EnumMeta"] return self.vm.make_class(node=node, name_var=cls_name_var, bases=[self.to_variable(node)], class_dict_var=cls_dict.to_variable(node), cls_var=metaclass, class_type=EnumInstance)
def _build_namedtuple(self, name, field_names, field_types, late_annots, node): # Build an InterpreterClass representing the namedtuple. if field_types: # TODO(mdemello): Fix this to support late types. field_types_union = abstract.Union(field_types, self.vm) else: field_types_union = self.vm.convert.none_type members = { n: t.instantiate(node) for n, t in moves.zip(field_names, field_types) } # collections.namedtuple has: __dict__, __slots__ and _fields. # typing.NamedTuple adds: _field_types, __annotations__ and _field_defaults. # __slots__ and _fields are tuples containing the names of the fields. slots = tuple( self.vm.convert.build_string(node, f) for f in field_names) members["__slots__"] = abstract.Tuple(slots, self.vm).to_variable(node) members["_fields"] = abstract.Tuple(slots, self.vm).to_variable(node) # __dict__ and _field_defaults are both collections.OrderedDicts that map # field names (strings) to objects of the field types. ordered_dict_cls = self.vm.convert.name_to_value( "collections.OrderedDict", ast=self.collections_ast) # In Python 2, keys can be `str` or `unicode`; support both. # In Python 3, `str_type` and `unicode_type` are the same. field_keys_union = abstract.Union( [self.vm.convert.str_type, self.vm.convert.unicode_type], self.vm) # Normally, we would use abstract_utils.K and abstract_utils.V, but # collections.pyi doesn't conform to that standard. field_dict_cls = abstract.ParameterizedClass(ordered_dict_cls, { "K": field_keys_union, "V": field_types_union }, self.vm) members["__dict__"] = field_dict_cls.instantiate(node) members["_field_defaults"] = field_dict_cls.instantiate(node) # _field_types and __annotations__ are both collections.OrderedDicts # that map field names (strings) to the types of the fields. field_types_cls = abstract.ParameterizedClass( ordered_dict_cls, { "K": field_keys_union, "V": self.vm.convert.type_type }, self.vm) members["_field_types"] = field_types_cls.instantiate(node) members["__annotations__"] = field_types_cls.instantiate(node) # __new__ # We set the bound on this TypeParameter later. This gives __new__ the # signature: def __new__(cls: Type[_Tname], ...) -> _Tname, i.e. the same # signature that visitor.CreateTypeParametersForSignatures would create. # This allows subclasses of the NamedTuple to get the correct type from # their constructors. cls_type_param = abstract.TypeParameter( visitors.CreateTypeParametersForSignatures.PREFIX + name, self.vm, bound=None) cls_type = abstract.ParameterizedClass( self.vm.convert.type_type, {abstract_utils.T: cls_type_param}, self.vm) # Use late annotations as field types if they exist. params = [ Param(n, late_annots.get(n, t)) for n, t in moves.zip(field_names, field_types) ] members["__new__"] = overlay_utils.make_method( self.vm, node, name="__new__", self_param=Param("cls", cls_type), params=params, return_type=cls_type_param, ) # __init__ members["__init__"] = overlay_utils.make_method(self.vm, node, name="__init__", varargs=Param("args"), kwargs=Param("kwargs")) # _make # _make is a classmethod, so it needs to be wrapped by # specialibuiltins.ClassMethodInstance. # Like __new__, it uses the _Tname TypeVar. sized_cls = self.vm.convert.name_to_value("typing.Sized") iterable_type = abstract.ParameterizedClass( self.vm.convert.name_to_value("typing.Iterable"), {abstract_utils.T: field_types_union}, self.vm) cls_type = abstract.ParameterizedClass( self.vm.convert.type_type, {abstract_utils.T: cls_type_param}, self.vm) len_type = abstract.CallableClass( self.vm.convert.name_to_value("typing.Callable"), { 0: sized_cls, abstract_utils.ARGS: sized_cls, abstract_utils.RET: self.vm.convert.int_type }, self.vm) params = [ Param("iterable", iterable_type), Param("new").unsolvable(self.vm, node), Param("len", len_type).unsolvable(self.vm, node) ] make = overlay_utils.make_method(self.vm, node, name="_make", params=params, self_param=Param("cls", cls_type), return_type=cls_type_param) make_args = function.Args(posargs=(make, )) _, members["_make"] = self.vm.special_builtins["classmethod"].call( node, None, make_args) # _replace # Like __new__, it uses the _Tname TypeVar. We have to annotate the `self` # param to make sure the TypeVar is substituted correctly. members["_replace"] = overlay_utils.make_method( self.vm, node, name="_replace", self_param=Param("self", cls_type_param), return_type=cls_type_param, kwargs=Param("kwds", field_types_union)) # __getnewargs__ getnewargs_tuple_params = dict( tuple(enumerate(field_types)) + ((abstract_utils.T, field_types_union), )) getnewargs_tuple = abstract.TupleClass(self.vm.convert.tuple_type, getnewargs_tuple_params, self.vm) members["__getnewargs__"] = overlay_utils.make_method( self.vm, node, name="__getnewargs__", return_type=getnewargs_tuple) # __getstate__ members["__getstate__"] = overlay_utils.make_method( self.vm, node, name="__getstate__") # _asdict members["_asdict"] = overlay_utils.make_method( self.vm, node, name="_asdict", return_type=field_dict_cls) # Finally, make the class. cls_dict = abstract.Dict(self.vm) cls_dict.update(node, members) if name.__class__ is compat.UnicodeType: # Unicode values should be ASCII. name = compat.native_str(name.encode("ascii")) node, cls_var = self.vm.make_class( node=node, name_var=self.vm.convert.build_string(node, name), bases=[self.vm.convert.tuple_type.to_variable(node)], class_dict_var=cls_dict.to_variable(node), cls_var=None) cls = cls_var.data[0] # Now that the class has been made, we can complete the TypeParameter used # by __new__, _make and _replace. cls_type_param.bound = cls # Add late annotations to the new class if late_annots: cls.late_annotations = late_annots self.vm.classes_with_late_annotations.append(cls) return node, cls_var
def new_dict(self, **kwargs): """Create a Dict from keywords mapping names to Variable objects.""" d = abstract.Dict(self._vm) for name, var in kwargs.items(): d.set_str_item(self._node, name, var) return d
def _build_namedtuple(self, name, field_names, field_types, node): # Build an InterpreterClass representing the namedtuple. if field_types: field_types_union = abstract.Union(field_types, self.vm) else: field_types_union = self.vm.convert.none_type members = { n: t.instantiate(node) for n, t in moves.zip(field_names, field_types) } # collections.namedtuple has: __dict__, __slots__ and _fields. # typing.NamedTuple adds: _field_types, __annotations__ and _field_defaults. # __slots__ and _fields are tuples containing the names of the fields. slots = tuple( self.vm.convert.build_string(node, f) for f in field_names) members["__slots__"] = abstract.Tuple(slots, self.vm).to_variable(node) members["_fields"] = abstract.Tuple(slots, self.vm).to_variable(node) # __dict__ and _field_defaults are both collections.OrderedDicts that map # field names (strings) to objects of the field types. ordered_dict_cls = self.vm.convert.name_to_value( "collections.OrderedDict", ast=self.collections_ast) # In Python 2, keys can be `str` or `unicode`; support both. # In Python 3, `str_type` and `unicode_type` are the same. field_keys_union = abstract.Union( [self.vm.convert.str_type, self.vm.convert.unicode_type], self.vm) # Normally, we would use abstract_utils.K and abstract_utils.V, but # collections.pyi doesn't conform to that standard. field_dict_cls = abstract.ParameterizedClass(ordered_dict_cls, { "K": field_keys_union, "V": field_types_union }, self.vm) members["__dict__"] = field_dict_cls.instantiate(node) members["_field_defaults"] = field_dict_cls.instantiate(node) # _field_types and __annotations__ are both collections.OrderedDicts # that map field names (strings) to the types of the fields. field_types_cls = abstract.ParameterizedClass( ordered_dict_cls, { "K": field_keys_union, "V": self.vm.convert.type_type }, self.vm) members["_field_types"] = field_types_cls.instantiate(node) members["__annotations__"] = field_types_cls.instantiate(node) # __new__ new_annots = {} new_lates = {} for (n, t) in moves.zip(field_names, field_types): # We don't support late annotations yet, but once we do, they'll show up # as LateAnnotation objects to be stored in new_lates. new_annots[n] = t # We set the bound on this TypeParameter later. This gives __new__ the # signature: def __new__(cls: Type[_Tname], ...) -> _Tname, i.e. the same # signature that visitor.CreateTypeParametersForSignatures would create. # This allows subclasses of the NamedTuple to get the correct type from # their constructors. cls_type_param = abstract.TypeParameter( visitors.CreateTypeParametersForSignatures.PREFIX + name, self.vm, bound=None) new_annots["cls"] = abstract.ParameterizedClass( self.vm.convert.type_type, {abstract_utils.T: cls_type_param}, self.vm) new_annots["return"] = cls_type_param members["__new__"] = abstract.SimpleFunction( name="__new__", param_names=("cls", ) + tuple(field_names), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations=new_annots, late_annotations=new_lates, vm=self.vm).to_variable(node) # __init__ members["__init__"] = abstract.SimpleFunction( name="__init__", param_names=("self", ), varargs_name="args", kwonly_params=(), kwargs_name="kwargs", defaults={}, annotations={}, late_annotations={}, vm=self.vm).to_variable(node) # _make # _make is a classmethod, so it needs to be wrapped by # specialibuiltins.ClassMethodInstance. # Like __new__, it uses the _Tname TypeVar. sized_cls = self.vm.convert.name_to_value("typing.Sized") iterable_type = abstract.ParameterizedClass( self.vm.convert.name_to_value("typing.Iterable"), {abstract_utils.T: field_types_union}, self.vm) make = abstract.SimpleFunction( name="_make", param_names=("cls", "iterable", "new", "len"), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={ "new": self.vm.convert.unsolvable.to_variable(node), "len": self.vm.convert.unsolvable.to_variable(node) }, annotations={ "cls": abstract.ParameterizedClass(self.vm.convert.type_type, {abstract_utils.T: cls_type_param}, self.vm), "iterable": iterable_type, "new": self.vm.convert.unsolvable, "len": abstract.Callable( self.vm.convert.name_to_value("typing.Callable"), { 0: sized_cls, abstract_utils.ARGS: sized_cls, abstract_utils.RET: self.vm.convert.int_type }, self.vm), "return": cls_type_param }, late_annotations={}, vm=self.vm).to_variable(node) make_args = function.Args(posargs=(make, )) _, members["_make"] = self.vm.special_builtins["classmethod"].call( node, None, make_args) # _replace # Like __new__, it uses the _Tname TypeVar. We have to annotate the `self` # param to make sure the TypeVar is substituted correctly. members["_replace"] = abstract.SimpleFunction( name="_replace", param_names=("self", ), varargs_name=None, kwonly_params=(), kwargs_name="kwds", defaults={}, annotations={ "self": cls_type_param, "kwds": field_types_union, "return": cls_type_param }, late_annotations={}, vm=self.vm).to_variable(node) # __getnewargs__ getnewargs_tuple_params = dict( tuple(enumerate(field_types)) + ((abstract_utils.T, field_types_union), )) getnewargs_tuple = abstract.TupleClass(self.vm.convert.tuple_type, getnewargs_tuple_params, self.vm) members["__getnewargs__"] = abstract.SimpleFunction( name="__getnewargs__", param_names=("self", ), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations={ "return": getnewargs_tuple }, late_annotations={}, vm=self.vm).to_variable(node) # __getstate__ members["__getstate__"] = abstract.SimpleFunction( name="__getstate__", param_names=("self", ), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations={}, late_annotations={}, vm=self.vm).to_variable(node) # _asdict members["_asdict"] = abstract.SimpleFunction( name="_asdict", param_names=("self", ), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations={ "return": field_dict_cls }, late_annotations={}, vm=self.vm).to_variable(node) # Finally, make the class. abs_membs = abstract.Dict(self.vm) abs_membs.update(node, members) cls_var = self.vm.make_class( node=node, name_var=self.vm.convert.build_string(node, name), bases=[self.vm.convert.tuple_type.to_variable(node)], class_dict_var=abs_membs.to_variable(node), cls_var=None) # Now that the class has been made, we can complete the TypeParameter used # by __new__, _make and _replace. cls_type_param.bound = cls_var.data[0] return cls_var