Example #1
0
  def add_scope(self, annot, types, module):
    """Add scope for type parameters.

    In original type class, all type parameters that should be added a scope
    will be replaced with a new copy.

    Args:
      annot: The type class.
      types: A type name list that should be added a scope.
      module: Module name.

    Returns:
      The type with fresh type parameters that have been added the scope.
    """
    if isinstance(annot, abstract.TypeParameter):
      if annot.name in types:
        new_annot = annot.copy()
        new_annot.module = module
        return new_annot
      return annot
    elif isinstance(annot, abstract.TupleClass):
      params = {}
      for name, param in annot.formal_type_parameters.items():
        params[name] = self.add_scope(param, types, module)
      return abstract.TupleClass(
          annot.base_cls, params, self.vm, annot.template)
    elif isinstance(annot, mixin.NestedAnnotation):
      inner_types = [(key, self.add_scope(typ, types, module))
                     for key, typ in annot.get_inner_types()]
      return annot.replace(inner_types)
    return annot
Example #2
0
 def testEmptyTupleClass(self):
   var = self.vm.program.NewVariable()
   params = {0: abstract.TypeParameter(abstract.K, self.vm),
             1: abstract.TypeParameter(abstract.V, self.vm)}
   params[abstract.T] = abstract.Union((params[0], params[1]), self.vm)
   right = abstract.TupleClass(self.vm.convert.tuple_type, params, self.vm)
   match = self.vm.matcher.match_var_against_type(
       var, right, {}, self.vm.root_cfg_node, {})
   self.assertSetEqual(set(match), {abstract.K, abstract.V})
Example #3
0
 def testUnsolvableAgainstTupleClass(self):
   left = self.vm.convert.unsolvable
   params = {0: abstract.TypeParameter(abstract.K, self.vm),
             1: abstract.TypeParameter(abstract.V, self.vm)}
   params[abstract.T] = abstract.Union((params[0], params[1]), self.vm)
   right = abstract.TupleClass(self.vm.convert.tuple_type, params, self.vm)
   for match in self._match_var(left, right):
     self.assertSetEqual(set(match), {abstract.K, abstract.V})
     self.assertEqual(match[abstract.K].data, [self.vm.convert.unsolvable])
     self.assertEqual(match[abstract.V].data, [self.vm.convert.unsolvable])
Example #4
0
 def testInstantiateTupleClassForSub(self):
   type_param = abstract.TypeParameter(abstract.K, self._vm)
   cls = abstract.TupleClass(self._vm.convert.tuple_type,
                             {0: type_param, abstract.T: type_param}, self._vm)
   # Instantiate the tuple class.
   subst_value = self._vm.annotations_util.instantiate_for_sub(
       self._vm.root_cfg_node, cls)
   # Recover the class from the instance.
   subbed_cls = self._vm.annotations_util.sub_one_annotation(
       self._vm.root_cfg_node, type_param, [{abstract.K: subst_value}])
   self.assertEqual(cls, subbed_cls)
Example #5
0
 def test_instantiate_tuple_class_for_sub(self):
   type_param = abstract.TypeParameter(abstract_utils.K, self._vm)
   cls = abstract.TupleClass(
       self._vm.convert.tuple_type,
       {0: type_param, abstract_utils.T: type_param}, self._vm)
   # Instantiate the tuple class.
   subst_value = cls.instantiate(self._vm.root_node,
                                 abstract_utils.DUMMY_CONTAINER)
   # Recover the class from the instance.
   subbed_cls = self._vm.annotations_util.sub_one_annotation(
       self._vm.root_node, type_param, [{
           abstract_utils.K: subst_value
       }])
   self.assertEqual(cls, subbed_cls)
Example #6
0
    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
Example #7
0
    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