示例#1
0
 def _make_func(self, name="_", param_names=None, varargs_name=None,
                kwonly_params=(), kwargs_name=None, defaults=(),
                annotations=None, late_annotations=None):
   return abstract.SimpleFunction(name, param_names or (), varargs_name,
                                  kwonly_params, kwargs_name, defaults,
                                  annotations or {}, late_annotations or {},
                                  self._vm)
示例#2
0
 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)
示例#3
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
示例#4
0
def make_method(vm,
                node,
                name,
                params=None,
                kwonly_params=None,
                return_type=None,
                self_param=None,
                varargs=None,
                kwargs=None):
    """Make a method from params.

  Args:
    vm: vm
    node: Node to create the method variable at
    name: The method name
    params: Positional params [type: [Param]]
    kwonly_params: Keyword only params [type: [Param]]
    return_type: Return type [type: PARAM_TYPES]
    self_param: Self param [type: Param, defaults to self: Any]
    varargs: Varargs param [type: Param, allows *args to be named and typed]
    kwargs: Kwargs param [type: Param, allows **kwargs to be named and typed]

  Returns:
    A new method wrapped in a variable.
  """
    def _process_annotation(param):
        """Process a single param into either annotations or late_annotations."""
        if not param.typ:
            return
        elif isinstance(param.typ, cfg.Variable):
            if all(t.cls for t in param.typ.data):
                types = param.typ.data
                if len(types) == 1:
                    annotations[param.name] = types[0].cls
                else:
                    t = abstract.Union([t.cls for t in types], vm)
                    annotations[param.name] = t
        elif isinstance(param.typ, abstract.LateAnnotation):
            late_annotations[param.name] = param.typ
        else:
            annotations[param.name] = param.typ

    # Set default values
    params = params or []
    kwonly_params = kwonly_params or []
    self_param = self_param or Param("self", None, None)
    annotations = {}
    late_annotations = {}

    params = [self_param] + params

    return_param = Param("return", return_type, None) if return_type else None
    special_params = [x for x in (return_param, varargs, kwargs) if x]
    for param in special_params + params + kwonly_params:
        _process_annotation(param)

    if vm.PY2:
        assert not kwonly_params, "kwonly_params is unsupported in python2"

    names = lambda xs: tuple(x.name for x in xs)
    param_names = names(params)
    kwonly_names = names(kwonly_params)
    defaults = {x.name: x.default for x in params if x.default}
    varargs_name = varargs.name if varargs else None
    kwargs_name = kwargs.name if kwargs else None

    ret = abstract.SimpleFunction(name=name,
                                  param_names=param_names,
                                  varargs_name=varargs_name,
                                  kwonly_params=kwonly_names,
                                  kwargs_name=kwargs_name,
                                  defaults=defaults,
                                  annotations=annotations,
                                  late_annotations=late_annotations,
                                  vm=vm)
    if late_annotations:
        vm.functions_with_late_annotations.append(ret)
    return ret.to_variable(node)
示例#5
0
def make_method(vm,
                node,
                name,
                params=None,
                kwonly_params=None,
                return_type=None,
                self_param=None,
                varargs=None,
                kwargs=None,
                kind=pytd.MethodTypes.METHOD):
    """Make a method from params.

  Args:
    vm: vm
    node: Node to create the method variable at
    name: The method name
    params: Positional params [type: [Param]]
    kwonly_params: Keyword only params [type: [Param]]
    return_type: Return type [type: PARAM_TYPES]
    self_param: Self param [type: Param, defaults to self: Any]
    varargs: Varargs param [type: Param, allows *args to be named and typed]
    kwargs: Kwargs param [type: Param, allows **kwargs to be named and typed]
    kind: The method kind

  Returns:
    A new method wrapped in a variable.
  """
    def _process_annotation(param):
        """Process a single param into annotations."""
        if not param.typ:
            return
        elif isinstance(param.typ, cfg.Variable):
            if all(t.cls for t in param.typ.data):
                types = param.typ.data
                if len(types) == 1:
                    annotations[param.name] = types[0].cls
                else:
                    t = abstract.Union([t.cls for t in types], vm)
                    annotations[param.name] = t
        else:
            annotations[param.name] = param.typ

    # Set default values
    params = params or []
    kwonly_params = kwonly_params or []
    if kind in (pytd.MethodTypes.METHOD, pytd.MethodTypes.PROPERTY):
        self_param = [self_param or Param("self", None, None)]
    elif kind == pytd.MethodTypes.CLASSMETHOD:
        self_param = [Param("cls", None, None)]
    else:
        assert kind == pytd.MethodTypes.STATICMETHOD
        self_param = []
    annotations = {}

    params = self_param + params

    return_param = Param("return", return_type, None) if return_type else None
    special_params = [x for x in (return_param, varargs, kwargs) if x]
    for param in special_params + params + kwonly_params:
        _process_annotation(param)

    if vm.PY2:
        assert not kwonly_params, "kwonly_params is unsupported in python2"

    names = lambda xs: tuple(x.name for x in xs)
    param_names = names(params)
    kwonly_names = names(kwonly_params)
    defaults = {x.name: x.default for x in params + kwonly_params if x.default}
    varargs_name = varargs.name if varargs else None
    kwargs_name = kwargs.name if kwargs else None

    ret = abstract.SimpleFunction(name=name,
                                  param_names=param_names,
                                  varargs_name=varargs_name,
                                  kwonly_params=kwonly_names,
                                  kwargs_name=kwargs_name,
                                  defaults=defaults,
                                  annotations=annotations,
                                  vm=vm)

    # Check that the constructed function has a valid signature
    bad_param = ret.signature.check_defaults()
    if bad_param:
        msg = "In method %s, non-default argument %s follows default argument" % (
            name, bad_param)
        vm.errorlog.invalid_function_definition(vm.frames, msg)

    retvar = ret.to_variable(node)
    if kind in (pytd.MethodTypes.METHOD, pytd.MethodTypes.PROPERTY):
        return retvar
    if kind == pytd.MethodTypes.CLASSMETHOD:
        decorator = vm.load_special_builtin("classmethod")
    else:
        assert kind == pytd.MethodTypes.STATICMETHOD
        decorator = vm.load_special_builtin("staticmethod")
    args = function.Args(posargs=(retvar, ))
    return decorator.call(node, funcv=None, args=args)[1]