Esempio n. 1
0
 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)
Esempio n. 2
0
 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)
Esempio n. 3
0
 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)
Esempio n. 4
0
 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)
Esempio n. 5
0
 def call(self, node, func, args):
     args = args.simplify(node, self.ctx)
     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.ctx.convert.constant_to_var(
             f"_NewType_Internal_Class_Name_{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.ctx.new_unsolvable(node)
     value_arg_name = "val"
     constructor = overlay_utils.make_method(
         self.ctx,
         node,
         name="__init__",
         params=[Param(value_arg_name, type_value)])
     members = abstract.Dict(self.ctx)
     members.set_str_item(node, "__init__", constructor)
     return self.ctx.make_class(node, name_arg, (type_arg, ),
                                members.to_variable(node), None)
Esempio n. 6
0
 def test_compatible_with__after_ambiguous_update(self):
     ambiguous_dict = abstract.Dict(self._ctx)
     ambiguous_dict.merge_instance_type_parameter(
         self._node, abstract_utils.K, self._ctx.new_unsolvable(self._node))
     ambiguous_dict.could_contain_anything = True
     self._d.update(self._node, ambiguous_dict)
     self.assertAmbiguous(self._d)
Esempio n. 7
0
def build_map_unpack(state, arg_list, ctx):
  """Merge a list of kw dicts into a single dict."""
  args = abstract.Dict(ctx)
  for arg in arg_list:
    for data in arg.data:
      args.update(state.node, data)
  args = args.to_variable(state.node)
  return args
Esempio n. 8
0
 def call(self, node, funcb, args):
     if self.ctx.options.build_dict_literals_from_kwargs:
         build_literal = not args.has_non_namedargs()
     else:
         build_literal = args.is_empty()
     if build_literal:
         # special-case a dict constructor with explicit k=v args
         d = abstract.Dict(self.ctx)
         for (k, v) in args.namedargs.items():
             d.set_str_item(node, k, v)
         return node, d.to_variable(node)
     else:
         return super().call(node, funcb, args)
Esempio n. 9
0
 def test_compatible_with__after_unambiguous_update(self):
     unambiguous_dict = abstract.Dict(self._ctx)
     unambiguous_dict.set_str_item(self._node, "a",
                                   self._ctx.new_unsolvable(self._node))
     self._d.update(self._node, unambiguous_dict)
     self.assertTruthy(self._d)
Esempio n. 10
0
 def test_compatible_with__after_empty_update(self):
     empty_dict = abstract.Dict(self._ctx)
     self._d.update(self._node, empty_dict)
     self.assertFalsy(self._d)
Esempio n. 11
0
 def setUp(self):
     super().setUp()
     self._d = abstract.Dict(self._ctx)
     self._var = self._program.NewVariable()
     self._var.AddBinding(abstract.Unknown(self._ctx), [], self._node)
Esempio n. 12
0
 def new_dict(self, **kwargs):
   """Create a Dict from keywords mapping names to Variable objects."""
   d = abstract.Dict(self._ctx)
   for name, var in kwargs.items():
     d.set_str_item(self._node, name, var)
   return d
Esempio n. 13
0
 def build_map(self, node):
   """Create an empty VM dict."""
   return abstract.Dict(self.ctx).to_variable(node)
Esempio n. 14
0
  def _build_namedtuple(self, name, field_names, field_types, node, bases):
    # 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.ctx)
    else:
      field_types_union = self.ctx.convert.none_type

    members = {n: t.instantiate(node) for n, t in 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.ctx.convert.build_string(node, f) for f in field_names)
    members["__slots__"] = self.ctx.convert.build_tuple(node, slots)
    members["_fields"] = self.ctx.convert.build_tuple(node, slots)
    # __dict__ and _field_defaults are both collections.OrderedDicts that map
    # field names (strings) to objects of the field types.
    ordered_dict_cls = self.ctx.convert.name_to_value(
        "collections.OrderedDict", ast=self.collections_ast)

    # 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": self.ctx.convert.str_type,
        "V": field_types_union
    }, self.ctx)
    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. Note that
    # ctx.make_class will take care of adding the __annotations__ member.
    field_types_cls = abstract.ParameterizedClass(ordered_dict_cls, {
        "K": self.ctx.convert.str_type,
        "V": self.ctx.convert.type_type
    }, self.ctx)
    members["_field_types"] = 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.ctx,
        bound=None)
    cls_type = abstract.ParameterizedClass(self.ctx.convert.type_type,
                                           {abstract_utils.T: cls_type_param},
                                           self.ctx)
    params = [Param(n, t) for n, t in zip(field_names, field_types)]
    members["__new__"] = overlay_utils.make_method(
        self.ctx,
        node,
        name="__new__",
        self_param=Param("cls", cls_type),
        params=params,
        return_type=cls_type_param,
    )

    # __init__
    members["__init__"] = overlay_utils.make_method(
        self.ctx,
        node,
        name="__init__",
        varargs=Param("args"),
        kwargs=Param("kwargs"))

    heterogeneous_tuple_type_params = dict(enumerate(field_types))
    heterogeneous_tuple_type_params[abstract_utils.T] = field_types_union
    # Representation of the to-be-created NamedTuple as a typing.Tuple.
    heterogeneous_tuple_type = abstract.TupleClass(
        self.ctx.convert.tuple_type, heterogeneous_tuple_type_params, self.ctx)

    # _make
    # _make is a classmethod, so it needs to be wrapped by
    # special_builtins.ClassMethodInstance.
    # Like __new__, it uses the _Tname TypeVar.
    sized_cls = self.ctx.convert.name_to_value("typing.Sized")
    iterable_type = abstract.ParameterizedClass(
        self.ctx.convert.name_to_value("typing.Iterable"),
        {abstract_utils.T: field_types_union}, self.ctx)
    cls_type = abstract.ParameterizedClass(self.ctx.convert.type_type,
                                           {abstract_utils.T: cls_type_param},
                                           self.ctx)
    len_type = abstract.CallableClass(
        self.ctx.convert.name_to_value("typing.Callable"), {
            0: sized_cls,
            abstract_utils.ARGS: sized_cls,
            abstract_utils.RET: self.ctx.convert.int_type
        }, self.ctx)
    params = [
        Param("iterable", iterable_type),
        Param("new").unsolvable(self.ctx, node),
        Param("len", len_type).unsolvable(self.ctx, node)
    ]
    make = overlay_utils.make_method(
        self.ctx,
        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.ctx.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.ctx,
        node,
        name="_replace",
        self_param=Param("self", cls_type_param),
        return_type=cls_type_param,
        kwargs=Param("kwds", field_types_union))

    # __getnewargs__
    members["__getnewargs__"] = overlay_utils.make_method(
        self.ctx,
        node,
        name="__getnewargs__",
        return_type=heterogeneous_tuple_type)

    # __getstate__
    members["__getstate__"] = overlay_utils.make_method(
        self.ctx, node, name="__getstate__")

    # _asdict
    members["_asdict"] = overlay_utils.make_method(
        self.ctx, node, name="_asdict", return_type=field_dict_cls)

    # Finally, make the class.
    cls_dict = abstract.Dict(self.ctx)
    cls_dict.update(node, members)

    if self.ctx.options.strict_namedtuple_checks:
      # Enforces type checking like Tuple[...]
      superclass_of_new_type = heterogeneous_tuple_type.to_variable(node)
    else:
      superclass_of_new_type = self.ctx.convert.tuple_type.to_variable(node)
    if bases:
      final_bases = []
      for base in bases:
        if any(b.full_name == "typing.NamedTuple" for b in base.data):
          final_bases.append(superclass_of_new_type)
        else:
          final_bases.append(base)
    else:
      final_bases = [superclass_of_new_type]
      # This NamedTuple is being created via a function call. We manually
      # construct an annotated_locals entry for it so that __annotations__ is
      # initialized properly for the generated class.
      self.ctx.vm.annotated_locals[name] = {
          field: abstract_utils.Local(node, None, typ, None, self.ctx)
          for field, typ in zip(field_names, field_types)
      }

    node, cls_var = self.ctx.make_class(
        node=node,
        name_var=self.ctx.convert.build_string(node, name),
        bases=final_bases,
        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

    return node, cls_var