Esempio n. 1
0
    def signature_to_callable(self, sig, vm):
        """Converts a function.Signature object into a callable object.

    Args:
      sig: The signature to convert.
      vm: The vm instance.

    Returns:
      An abstract.Callable representing the signature, or an
      abstract.ParameterizedClass if the signature has a variable number of
      arguments.
    """
        base_cls = vm.convert.function_type
        ret = sig.annotations.get("return", vm.convert.unsolvable)
        if self._detailed or (sig.mandatory_param_count()
                              == sig.maximum_param_count()):
            # If self._detailed is false, we throw away the argument types if the
            # function takes a variable number of arguments, which is correct for pyi
            # generation but undesirable for, say, error message printing.
            args = [
                sig.annotations.get(name, vm.convert.unsolvable)
                for name in sig.param_names
            ]
            params = {
                abstract.ARGS: abstract.merge_values(args, vm),
                abstract.RET: ret
            }
            params.update(enumerate(args))
            return abstract.Callable(base_cls, params, vm)
        else:
            # The only way to indicate a variable number of arguments in a Callable
            # is to not specify argument types at all.
            params = {abstract.ARGS: vm.convert.unsolvable, abstract.RET: ret}
            return abstract.ParameterizedClass(base_cls, params, vm)
Esempio n. 2
0
 def _filter_and_merge_candidates(self, node, candidates):
     """Merge the given candidates into one variable, filtered by the node."""
     ret = self.vm.program.NewVariable()
     for candidate in candidates:
         for binding in candidate.Bindings(node):
             val = binding.data
             if isinstance(val, abstract.TypeParameterInstance):
                 var = val.instance.type_parameters[val.name]
                 # If this type parameter has visible values, we add those to the
                 # return value. Otherwise, if it has constraints, we add those as an
                 # upper bound on the values. When all else fails, we add an empty
                 # value as a placeholder that can be passed around and converted to
                 # Any after analysis.
                 if var.bindings:
                     candidates.append(var)
                 elif val.param.constraints:
                     constraints = abstract.merge_values(
                         val.param.constraints, self.vm)
                     ret.PasteVariable(constraints.instantiate(node))
                 else:
                     ret.AddBinding(self.vm.convert.empty, [], node)
             else:
                 sources = {binding}
                 ret.AddBinding(val, sources, node)
     if ret.bindings:
         return ret
     else:
         return None
Esempio n. 3
0
 def attribute_error(self, opcode, obj, attr_name):
     assert obj.bindings
     obj_values = abstract.merge_values(obj.data, obj.bindings[0].data.vm)
     if isinstance(obj_values, abstract.Module):
         obj_repr = "module %r" % obj_values.name
     else:
         obj_repr = self._print_as_actual_type(obj_values)
     self.error(opcode, "No attribute %r on %s" % (attr_name, obj_repr))
Esempio n. 4
0
 def _get_value_info(self, inner, ends_with_ellipsis):
     if not ends_with_ellipsis:
         template = range(len(inner)) + [abstract.T]
         inner += (abstract.merge_values(inner, self.vm), )
         return template, inner, abstract.TupleClass
     else:
         return super(Tuple, self)._get_value_info(inner,
                                                   ends_with_ellipsis)
Esempio n. 5
0
 def _get_value_info(self, inner, ellipses):
     if ellipses:
         # An ellipsis may appear at the end of the parameter list as long as it is
         # not the only parameter.
         return super(Tuple, self)._get_value_info(
             inner, ellipses, allowed_ellipses={len(inner) - 1} - {0})
     else:
         template = list(moves.range(len(inner))) + [abstract.T]
         inner += (abstract.merge_values(inner, self.vm), )
         return template, inner, abstract.TupleClass
Esempio n. 6
0
 def _get_value_info(self, inner, ends_with_ellipsis):
     if isinstance(inner[0], list):
         template = range(len(
             inner[0])) + [t.name for t in self.base_cls.template]
         combined_args = abstract.merge_values(inner[0], self.vm)
         inner = tuple(inner[0]) + (combined_args, ) + inner[1:]
         return template, inner, abstract.Callable
     else:
         return super(Callable,
                      self)._get_value_info(inner, ends_with_ellipsis)
Esempio n. 7
0
 def _attribute_error(self, stack, binding, attr_name):
     obj_repr = self._print_as_actual_type(binding.data)
     if len(binding.variable.bindings) > 1:
         details = "In %s" % self._print_as_actual_type(
             abstract.merge_values(binding.variable.data, binding.data.vm))
     else:
         details = None
     self.error(stack,
                "No attribute %r on %s" % (attr_name, obj_repr),
                details=details)
Esempio n. 8
0
 def _get_value_info(self, inner, ellipses):
   if isinstance(inner[0], list):
     template = (list(moves.range(len(inner[0]))) +
                 [t.name for t in self.base_cls.template])
     combined_args = abstract.merge_values(inner[0], self.vm)
     inner = tuple(inner[0]) + (combined_args,) + inner[1:]
     self.vm.errorlog.invalid_ellipses(self.vm.frames, ellipses, self.name)
     return template, inner, abstract.Callable
   else:
     # An ellipsis may take the place of the ARGS list.
     return super(Callable, self)._get_value_info(
         inner, ellipses, allowed_ellipses={0})
Esempio n. 9
0
 def call(self, node, func, args):
     if args.posargs:
         try:
             annot = self.vm.annotations_util.process_annotation_var(
                 args.posargs[0], "typing.cast", self.vm.frames, node)
         except self.vm.annotations_util.LateAnnotationError:
             self.vm.errorlog.invalid_annotation(
                 self.vm.frames,
                 abstract.merge_values(args.posargs[0].data, self.vm),
                 "Forward references not allowed in typing.cast.\n"
                 "Consider switching to a type comment.")
             annot = self.vm.convert.create_new_unsolvable(node)
         args = args.replace(posargs=(annot, ) + args.posargs[1:])
     return super(Cast, self).call(node, func, args)
Esempio n. 10
0
    def merge_classes(self, node, instances):
        """Merge the classes of the given instances.

    Args:
      node: The current node.
      instances: An iterable of instances.
    Returns:
      An abstract.AtomicAbstractValue created by merging the instances' classes.
    """
        classes = set()
        for v in instances:
            cls = v.get_class()
            if cls:
                classes.add(cls)
        return abstract.merge_values(classes, self.vm)
Esempio n. 11
0
  def _filter_var(self, node, var):
    """Filter the variable by the node.

    Filters the variable data, including recursively expanded type parameter
    instances, by visibility at the node. A type parameter instance needs to be
    filtered at the moment of access because its value may change later.

    Args:
      node: The current node.
      var: A variable to filter.
    Returns:
      The filtered variable.
    """
    # First, check if we need to do any filtering at all. This method is
    # heavily called, so creating the `ret` variable judiciously reduces the
    # number of variables per pytype run by as much as 20%.
    bindings = var.Bindings(node)
    if not bindings:
      return None
    if len(bindings) == len(var.bindings) and not any(
        isinstance(b.data, abstract.TypeParameterInstance) for b in bindings):
      return var
    ret = self.vm.program.NewVariable()
    for binding in bindings:
      val = binding.data
      if isinstance(val, abstract.TypeParameterInstance):
        var = val.instance.type_parameters[val.name]
        # If this type parameter has visible values, we add those to the
        # return value. Otherwise, if it has constraints, we add those as an
        # upper bound on the values. When all else fails, we add an empty
        # value as a placeholder that can be passed around and converted to
        # Any after analysis.
        var_bindings = var.Bindings(node)
        if var_bindings:
          bindings.extend(var_bindings)
        elif val.param.constraints:
          constraints = abstract.merge_values(val.param.constraints, self.vm)
          ret.PasteVariable(constraints.instantiate(node))
        else:
          ret.AddBinding(self.vm.convert.empty, [], node)
      else:
        ret.AddBinding(val, {binding}, node)
    if ret.bindings:
      return ret
    else:
      return None
Esempio n. 12
0
 def not_writable(self, stack, obj, attr_name):
   obj_values = abstract.merge_values([obj], obj.vm)
   obj_repr = self._print_as_actual_type(obj_values)
   self.error(stack, "Can't assign attribute %r on %s" % (attr_name, obj_repr))
Esempio n. 13
0
 def attribute_error(self, stack, obj, attr_name):
     assert obj.bindings
     obj_values = abstract.merge_values(obj.data, obj.data[0].vm)
     obj_repr = self._print_as_actual_type(obj_values)
     self.error(stack, "No attribute %r on %s" % (attr_name, obj_repr))
Esempio n. 14
0
    def _constant_to_value(self, pyval, subst, get_node):
        """Create a AtomicAbstractValue that represents a python constant.

    This supports both constant from code constant pools and PyTD constants such
    as classes. This also supports builtin python objects such as int and float.

    Args:
      pyval: The python or PyTD value to convert.
      subst: The current type parameters.
      get_node: A getter function for the current node.
    Returns:
      A Value that represents the constant, or None if we couldn't convert.
    Raises:
      NotImplementedError: If we don't know how to convert a value.
      TypeParameterError: If we can't find a substitution for a type parameter.
    """
        if isinstance(pyval, str):
            return abstract.AbstractOrConcreteValue(pyval, self.str_type,
                                                    self.vm)
        elif isinstance(pyval, int) and -1 <= pyval <= MAX_IMPORT_DEPTH:
            # For small integers, preserve the actual value (for things like the
            # level in IMPORT_NAME).
            return abstract.AbstractOrConcreteValue(pyval, self.int_type,
                                                    self.vm)
        elif isinstance(pyval, long):
            # long is aliased to int
            return self.primitive_class_instances[int]
        elif pyval.__class__ in self.primitive_classes:
            return self.primitive_class_instances[pyval.__class__]
        elif isinstance(pyval, (loadmarshal.CodeType, blocks.OrderedCode)):
            return abstract.AbstractOrConcreteValue(
                pyval, self.primitive_classes[types.CodeType], self.vm)
        elif pyval is super:
            return special_builtins.Super(self.vm)
        elif pyval is object:
            return special_builtins.Object(self.vm)
        elif pyval.__class__ is type:
            if pyval is types.FunctionType:
                classname = "typing.Callable"
            else:
                classname = "__builtin__." + pyval.__name__
            try:
                return self.name_to_value(classname, subst)
            except (KeyError, AttributeError):
                log.debug("Failed to find pytd", exc_info=True)
                raise
        elif isinstance(pyval, pytd.TypeDeclUnit):
            data = (pyval.constants + pyval.type_params + pyval.classes +
                    pyval.functions + pyval.aliases)
            members = {val.name.rsplit(".")[-1]: val for val in data}
            return abstract.Module(self.vm, pyval.name, members, pyval)
        elif isinstance(pyval,
                        pytd.Class) and pyval.name == "__builtin__.super":
            return self.vm.special_builtins["super"]
        elif isinstance(pyval,
                        pytd.Class) and pyval.name == "__builtin__.object":
            return self.object_type
        elif isinstance(pyval,
                        pytd.Class) and pyval.name == "types.ModuleType":
            return self.module_type
        elif isinstance(pyval, pytd.Class):
            module, dot, base_name = pyval.name.rpartition(".")
            try:
                cls = abstract.PyTDClass(base_name, pyval, self.vm)
            except mro.MROError as e:
                self.vm.errorlog.mro_error(self.vm.frames, base_name,
                                           e.mro_seqs)
                cls = self.unsolvable
            else:
                if dot:
                    cls.module = module
            return cls
        elif isinstance(pyval, pytd.ExternalFunction):
            module, _, name = pyval.name.partition(".")
            assert module == "__builtin__", "PYTHONCODE allowed only in __builtin__"
            return abstract.merge_values(
                self.vm.frame.f_globals.members[name].data, self.vm)
        elif isinstance(pyval, pytd.Function):
            signatures = [
                abstract.PyTDSignature(pyval.name, sig, self.vm)
                for sig in pyval.signatures
            ]
            type_new = self.vm.lookup_builtin("__builtin__.type").Lookup(
                "__new__")
            if pyval is type_new:
                f_cls = special_builtins.TypeNew
            else:
                f_cls = abstract.PyTDFunction
            f = f_cls(pyval.name, signatures, pyval.kind, self.vm)
            f.is_abstract = pyval.is_abstract
            return f
        elif isinstance(pyval, pytd.ClassType):
            assert pyval.cls
            return self.constant_to_value(pyval.cls, subst,
                                          self.vm.root_cfg_node)
        elif isinstance(pyval, pytd.NothingType):
            return self.nothing
        elif isinstance(pyval, pytd.AnythingType):
            return self.unsolvable
        elif isinstance(pyval, pytd.FunctionType):
            return self.constant_to_value(pyval.function, subst,
                                          self.vm.root_cfg_node)
        elif isinstance(pyval, pytd.UnionType):
            options = [
                self.constant_to_value(t, subst, self.vm.root_cfg_node)
                for t in pyval.type_list
            ]
            if len(options) > 1:
                return abstract.Union(options, self.vm)
            else:
                return options[0]
        elif isinstance(pyval, pytd.TypeParameter):
            constraints = tuple(
                self.constant_to_value(c, {}, self.vm.root_cfg_node)
                for c in pyval.constraints)
            bound = (pyval.bound and self.constant_to_value(
                pyval.bound, {}, self.vm.root_cfg_node))
            return abstract.TypeParameter(pyval.name,
                                          self.vm,
                                          constraints=constraints,
                                          bound=bound)
        elif isinstance(pyval, abstract.AsInstance):
            cls = pyval.cls
            if isinstance(cls, pytd.ClassType):
                cls = cls.cls
            if isinstance(cls, pytd.Class):
                # This key is also used in __init__
                key = (abstract.Instance, cls)
                if key not in self._convert_cache:
                    if cls.name in [
                            "__builtin__.type", "__builtin__.property"
                    ]:
                        # An instance of "type" or of an anonymous property can be anything.
                        instance = self._create_new_unknown_value("type")
                    else:
                        mycls = self.constant_to_value(cls, subst,
                                                       self.vm.root_cfg_node)
                        instance = abstract.Instance(mycls, self.vm)
                        instance.make_template_unsolvable(
                            cls.template, self.vm.root_cfg_node)
                    log.info("New pytd instance for %s: %r", cls.name,
                             instance)
                    self._convert_cache[key] = instance
                return self._convert_cache[key]
            elif isinstance(cls, pytd.GenericType):
                assert isinstance(cls.base_type, pytd.ClassType)
                base_cls = cls.base_type.cls
                if base_cls.name == "__builtin__.type":
                    c, = cls.parameters
                    if isinstance(c, pytd.TypeParameter):
                        if not subst or c.name not in subst:
                            raise self.TypeParameterError(c.name)
                        return self.merge_classes(get_node(),
                                                  subst[c.name].data)
                    else:
                        return self.constant_to_value(c, subst,
                                                      self.vm.root_cfg_node)
                elif isinstance(cls, pytd.TupleType):
                    content = tuple(
                        self.constant_to_var(abstract.AsInstance(p), subst,
                                             get_node())
                        for p in cls.parameters)
                    return abstract.Tuple(content, self.vm)
                elif isinstance(cls, pytd.CallableType):
                    clsval = self.constant_to_value(cls, subst,
                                                    self.vm.root_cfg_node)
                    return abstract.Instance(clsval, self.vm)
                else:
                    clsval = self.constant_to_value(base_cls, subst,
                                                    self.vm.root_cfg_node)
                    instance = abstract.Instance(clsval, self.vm)
                    assert len(cls.parameters) <= len(base_cls.template)
                    for formal, actual in zip(base_cls.template,
                                              cls.parameters):
                        p = self.constant_to_var(abstract.AsInstance(actual),
                                                 subst, self.vm.root_cfg_node)
                        instance.initialize_type_parameter(
                            get_node(), formal.name, p)
                    return instance
            else:
                return self.constant_to_value(cls, subst,
                                              self.vm.root_cfg_node)
        elif isinstance(pyval, pytd.GenericType):
            assert isinstance(pyval.base_type, pytd.ClassType)
            base_cls = self.constant_to_value(pyval.base_type.cls, subst,
                                              self.vm.root_cfg_node)
            if not isinstance(base_cls, abstract.Class):
                # base_cls can be, e.g., an unsolvable due to an mro error.
                return self.unsolvable
            if isinstance(pyval, pytd.TupleType):
                abstract_class = abstract.TupleClass
                template = range(len(pyval.parameters)) + [abstract.T]
                parameters = pyval.parameters + (pytd.UnionType(
                    pyval.parameters), )
            elif isinstance(pyval, pytd.CallableType):
                abstract_class = abstract.Callable
                template = range(len(
                    pyval.args)) + [abstract.ARGS, abstract.RET]
                parameters = pyval.args + (pytd_utils.JoinTypes(
                    pyval.args), pyval.ret)
            else:
                abstract_class = abstract.ParameterizedClass
                template = tuple(t.name for t in pyval.base_type.cls.template)
                parameters = pyval.parameters
            assert (pyval.base_type.name == "typing.Generic"
                    or len(parameters) <= len(template))
            type_parameters = utils.LazyDict()
            for i, name in enumerate(template):
                if i < len(parameters):
                    type_parameters.add_lazy_item(name, self.constant_to_value,
                                                  parameters[i], subst,
                                                  self.vm.root_cfg_node)
                else:
                    type_parameters[name] = self.unsolvable
            return abstract_class(base_cls, type_parameters, self.vm)
        elif pyval.__class__ is tuple:  # only match raw tuple, not namedtuple/Node
            return self.tuple_to_value([
                self.constant_to_var(item, subst, self.vm.root_cfg_node)
                for i, item in enumerate(pyval)
            ])
        else:
            raise NotImplementedError("Can't convert constant %s %r" %
                                      (type(pyval), pyval))
Esempio n. 15
0
 def attribute_error(self, opcode, obj, attr_name):
     assert obj.bindings
     obj_values = self._print_as_actual_type(
         abstract.merge_values(obj.data, obj.bindings[0].data.vm))
     self.error(opcode, "No attribute %r on %s" % (attr_name, obj_values))