Exemplo n.º 1
0
 def test_heterogeneous_tuple(self):
     ast = self._load_ast(
         "a", """
   from typing import Tuple
   x = ...  # type: Tuple[str, int]
 """)
     x = ast.Lookup("a.x").type
     cls = self._vm.convert.constant_to_value("x", x, {},
                                              self._vm.root_cfg_node)
     instance = self._vm.convert.constant_to_value("x",
                                                   abstract.AsInstance(x),
                                                   {},
                                                   self._vm.root_cfg_node)
     self.assertIsInstance(cls, abstract.TupleClass)
     self.assertListEqual(sorted(cls.type_parameters.items()),
                          [(0, self._vm.convert.str_type.data[0]),
                           (1, self._vm.convert.int_type.data[0]),
                           (abstract.T,
                            abstract.Union([
                                cls.type_parameters[0],
                                cls.type_parameters[1],
                            ], self._vm))])
     self.assertIsInstance(instance, abstract.Tuple)
     self.assertListEqual(
         [v.data for v in instance.pyval],
         [[self._vm.convert.primitive_class_instances[str]],
          [self._vm.convert.primitive_class_instances[int]]])
     self.assertListEqual(instance.type_parameters[abstract.T].data, [
         self._vm.convert.primitive_class_instances[str],
         self._vm.convert.primitive_class_instances[int]
     ])
Exemplo n.º 2
0
 def test_callable_with_args(self):
     ast = self._load_ast(
         "a", """
   from typing import Callable
   x = ...  # type: Callable[[int, bool], str]
 """)
     x = ast.Lookup("a.x").type
     cls = self._vm.convert.constant_to_value(x, {}, self._vm.root_cfg_node)
     instance = self._vm.convert.constant_to_value(abstract.AsInstance(x),
                                                   {},
                                                   self._vm.root_cfg_node)
     self.assertIsInstance(cls, abstract.Callable)
     self.assertItemsEqual(
         cls.type_parameters.items(),
         [(0, self._vm.convert.int_type),
          (1, self._vm.convert.primitive_classes[bool]),
          (abstract.ARGS,
           abstract.Union([cls.type_parameters[0], cls.type_parameters[1]],
                          self._vm)),
          (abstract.RET, self._vm.convert.str_type)])
     self.assertIsInstance(instance, abstract.Instance)
     self.assertEqual(abstract.get_atomic_value(instance.cls), cls)
     self.assertItemsEqual([
         (name, set(var.data))
         for name, var in instance.type_parameters.items()
     ], [(abstract.ARGS, {
         self._vm.convert.primitive_class_instances[int],
         self._vm.convert.primitive_class_instances[bool]
     }), (abstract.RET, {self._vm.convert.primitive_class_instances[str]})])
Exemplo n.º 3
0
 def _create_call_arg(self, name, t, node):
   if t == pytd.ClassType("__builtin__.object"):
     # As an arg, "object" means: we can use anything for this argument,
     # because everything inherits from object.
     # TODO(kramm): Maybe we should use AnythingType for params without type.
     return self.convert.create_new_unsolvable(node, name)
   else:
     return self.convert.convert_constant(
         name, abstract.AsInstance(t), subst={}, node=self.root_cfg_node)
Exemplo n.º 4
0
 def test_classvar_instance(self):
   ast = self._load_ast("a", """
     from typing import ClassVar
     class X:
       v: ClassVar[int]
   """)
   pyval = ast.Lookup("a.X").Lookup("v").type
   v = self._vm.convert.constant_to_value(
       abstract.AsInstance(pyval), {}, self._vm.root_cfg_node)
   self.assertEqual(v, self._vm.convert.primitive_class_instances[int])
Exemplo n.º 5
0
 def test_callable_no_args(self):
   ast = self._load_ast("a", """
     from typing import Callable
     x = ... # type: Callable[[], ...]
   """)
   x = ast.Lookup("a.x").type
   cls = self._vm.convert.constant_to_value(x, {}, self._vm.root_cfg_node)
   instance = self._vm.convert.constant_to_value(
       abstract.AsInstance(x), {}, self._vm.root_cfg_node)
   self.assertIsInstance(cls.type_parameters[abstract.ARGS], abstract.Empty)
   self.assertEqual(
       abstract.get_atomic_value(instance.type_parameters[abstract.ARGS]),
       self._vm.convert.empty)
Exemplo n.º 6
0
 def test_plain_callable(self):
   ast = self._load_ast("a", """
     from typing import Callable
     x = ...  # type: Callable[..., int]
   """)
   x = ast.Lookup("a.x").type
   cls = self._vm.convert.constant_to_value(x, {}, self._vm.root_cfg_node)
   instance = self._vm.convert.constant_to_value(
       abstract.AsInstance(x), {}, self._vm.root_cfg_node)
   self.assertIsInstance(cls, abstract.ParameterizedClass)
   self.assertItemsEqual(cls.type_parameters.items(),
                         [(abstract.ARGS, self._vm.convert.unsolvable),
                          (abstract.RET, self._vm.convert.int_type)])
   self.assertIsInstance(instance, abstract.Instance)
   self.assertEqual(abstract.get_atomic_value(instance.cls), cls.base_cls)
   self.assertItemsEqual(
       [(name, var.data) for name, var in instance.type_parameters.items()],
       [(abstract.ARGS, [self._vm.convert.unsolvable]),
        (abstract.RET, [self._vm.convert.primitive_class_instances[int]])])
Exemplo n.º 7
0
 def testTupleSubclass(self):
   subclass = self._parse_and_lookup("""
     from typing import Tuple
     class A(Tuple[bool, int]): ...""", "A")
   left = self.vm.convert.constant_to_value(
       "", abstract.AsInstance(subclass), {}, self.vm.root_cfg_node)
   right1 = self._convert("Tuple[bool, int]", as_instance=False)
   right2 = self._convert("Tuple[int, bool]", as_instance=False)
   right3 = self._convert("Tuple[int, int]", as_instance=False)
   right4 = self._convert("Tuple[int]", as_instance=False)
   right5 = self._convert("tuple", as_instance=False)
   right6 = self._convert("Tuple[bool, ...]", as_instance=False)
   right7 = self._convert("Tuple[int, ...]", as_instance=False)
   self.assertMatch(left, right1)
   self.assertNoMatch(left, right2)
   self.assertMatch(left, right3)
   self.assertNoMatch(left, right4)
   self.assertMatch(left, right5)
   self.assertNoMatch(left, right6)
   self.assertMatch(left, right7)
Exemplo n.º 8
0
  def _convert(self, t, as_instance):
    """Convenience function for turning a string into an abstract value.

    Note that this function cannot be called more than once per test with
    the same arguments, since we hash the arguments to get a filename for
    the temporary pyi.

    Args:
      t: The string representation of a type.
      as_instance: Whether to convert as an instance.

    Returns:
      An AtomicAbstractValue.
    """
    src = "from typing import Tuple, Type\n"
    src += "x = ...  # type: " + t
    filename = str(hash((t, as_instance)))
    x = self._parse_and_lookup(src, "x", filename).type
    if as_instance:
      x = abstract.AsInstance(x)
    return self.vm.convert.constant_to_value("", x, {}, self.vm.root_cfg_node)
Exemplo n.º 9
0
 def call(self, node, func, args):
     if self.vm.PY3:
         # In Python 3, the type of IO object returned depends on the mode.
         self.match_args(node, args)  # May raise FailedFunctionCall.
         sig, = self.signatures
         callargs = {
             name: var
             for name, var, _ in sig.signature.iter_args(args)
         }
         try:
             if "mode" not in callargs:
                 io_type = "Text"  # The default mode is 'r'.
             else:
                 mode = abstract.get_atomic_python_constant(
                     callargs["mode"])
                 io_type = "Binary" if "b" in mode else "Text"
         except abstract.ConversionError:
             pass
         else:
             return node, self.vm.convert.constant_to_var(
                 abstract.AsInstance(
                     self.vm.lookup_builtin("typing.%sIO" % io_type)), {},
                 node)
     return super(Open, self).call(node, func, args)
Exemplo n.º 10
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 pyval.__class__ is str:
            # We use a subclass of str, compat.BytesPy3, to mark Python 3
            # bytestrings, which are converted to abstract bytes instances.
            # compat.BytesType dispatches to this when appropriate.
            return abstract.AbstractOrConcreteValue(pyval, self.str_type,
                                                    self.vm)
        elif isinstance(pyval, compat.UnicodeType):
            return abstract.AbstractOrConcreteValue(pyval, self.unicode_type,
                                                    self.vm)
        elif isinstance(pyval, compat.BytesType):
            return abstract.AbstractOrConcreteValue(pyval, self.bytes_type,
                                                    self.vm)
        elif isinstance(pyval, bool):
            return self.true if pyval is True else self.false
        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, compat.LongType):
            # 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:
            try:
                return self.name_to_value(self._type_to_name(pyval), subst)
            except (KeyError, AttributeError):
                log.debug("Failed to find pytd", exc_info=True)
                raise
        elif isinstance(pyval, pytd.LateType):
            actual = self._load_late_type(pyval)
            return self._constant_to_value(actual, subst, get_node)
        elif isinstance(pyval, pytd.TypeDeclUnit):
            return self._create_module(pyval)
        elif isinstance(pyval, pytd.Module):
            mod = self.vm.loader.import_name(pyval.module_name)
            return self._create_module(mod)
        elif isinstance(pyval, pytd.Class):
            if pyval.name == "__builtin__.super":
                return self.vm.special_builtins["super"]
            elif pyval.name == "__builtin__.object":
                return self.object_type
            elif pyval.name == "types.ModuleType":
                return self.module_type
            elif pyval.name == "_importlib_modulespec.ModuleType":
                # Python 3's typeshed uses a stub file indirection to define ModuleType
                # even though it is exported via types.pyi.
                return self.module_type
            else:
                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.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.empty
        elif isinstance(pyval, pytd.AnythingType):
            return self.unsolvable
        elif (isinstance(pyval, pytd.Constant)
              and isinstance(pyval.type, pytd.AnythingType)):
            # We allow "X = ... # type: Any" to declare X as a type.
            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.LateType):
                actual = self._load_late_type(cls)
                if not isinstance(actual, pytd.ClassType):
                    return self.unsolvable
                cls = actual.cls
            if isinstance(cls, pytd.ClassType):
                cls = cls.cls
            if (isinstance(cls, pytd.GenericType)
                    and cls.base_type.name == "typing.ClassVar"):
                param, = cls.parameters
                return self.constant_to_value(abstract.AsInstance(param),
                                              subst, self.vm.root_cfg_node)
            elif isinstance(cls,
                            pytd.GenericType) or (isinstance(cls, pytd.Class)
                                                  and cls.template):
                # If we're converting a generic Class, need to create a new instance of
                # it. See test_classes.testGenericReinstantiated.
                if isinstance(cls, pytd.Class):
                    params = tuple(t.type_param.upper_value
                                   for t in cls.template)
                    cls = pytd.GenericType(base_type=pytd.ClassType(
                        cls.name, cls),
                                           parameters=params)
                if isinstance(cls.base_type, pytd.LateType):
                    actual = self._load_late_type(cls.base_type)
                    if not isinstance(actual, pytd.ClassType):
                        return self.unsolvable
                    base_cls = actual.cls
                else:
                    assert isinstance(cls.base_type, pytd.ClassType)
                    base_cls = cls.base_type.cls
                assert isinstance(base_cls, pytd.Class), base_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)
                    num_params = len(cls.parameters)
                    assert num_params <= len(base_cls.template)
                    for i, formal in enumerate(base_cls.template):
                        if i < num_params:
                            node = get_node()
                            p = self.constant_to_var(
                                abstract.AsInstance(cls.parameters[i]), subst,
                                node)
                        else:
                            # An omitted type parameter implies `Any`.
                            node = self.vm.root_cfg_node
                            p = self.unsolvable.to_variable(node)
                        instance.merge_type_parameter(node, formal.name, p)
                    return instance
            elif isinstance(cls, pytd.Class):
                assert not cls.template
                # 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)
                    log.info("New pytd instance for %s: %r", cls.name,
                             instance)
                    self._convert_cache[key] = instance
                return self._convert_cache[key]
            else:
                return self.constant_to_value(cls, subst,
                                              self.vm.root_cfg_node)
        elif (isinstance(pyval, pytd.GenericType)
              and pyval.base_type.name == "typing.ClassVar"):
            param, = pyval.parameters
            return self.constant_to_value(param, subst, self.vm.root_cfg_node)
        elif isinstance(pyval, pytd.GenericType):
            if isinstance(pyval.base_type, pytd.LateType):
                actual = self._load_late_type(pyval.base_type)
                if not isinstance(actual, pytd.ClassType):
                    return self.unsolvable
                base = actual.cls
            else:
                assert isinstance(pyval.base_type, pytd.ClassType)
                base = pyval.base_type.cls
            assert isinstance(base, pytd.Class), base
            base_cls = self.constant_to_value(base, 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 = list(range(len(pyval.parameters))) + [abstract.T]
                parameters = pyval.parameters + (pytd.UnionType(
                    pyval.parameters), )
            elif isinstance(pyval, pytd.CallableType):
                abstract_class = abstract.Callable
                template = list(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 base.template)
                parameters = pyval.parameters
            assert (pyval.base_type.name == "typing.Generic"
                    or len(parameters) <= len(template))
            type_parameters = datatypes.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))
Exemplo n.º 11
0
    def constant_to_var(self,
                        pyval,
                        subst=None,
                        node=None,
                        source_sets=None,
                        discard_concrete_values=False):
        """Convert a constant to a Variable.

    This converts a constant to a cfg.Variable. Unlike constant_to_value, it
    can handle things that need to be represented as a Variable with multiple
    possible values (i.e., a union type), like pytd.Function.

    Args:
      pyval: The Python constant to convert. Can be a PyTD definition or a
        builtin constant.
      subst: The current type parameters.
      node: The current CFG node. (For instances)
      source_sets: An iterator over instances of SourceSet (or just tuples).
      discard_concrete_values: Whether concrete values should be discarded from
        type parameters.
    Returns:
      A cfg.Variable.
    Raises:
      TypeParameterError: if conversion is attempted on a type parameter without
        a substitution.
      ValueError: if pytype is not of a known type.
    """
        source_sets = source_sets or [[]]
        node = node or self.vm.root_cfg_node
        if isinstance(pyval, pytd.NothingType):
            return self.vm.program.NewVariable([], [], self.vm.root_cfg_node)
        elif isinstance(pyval, pytd.Alias):
            return self.constant_to_var(pyval.type, subst, node, source_sets,
                                        discard_concrete_values)
        elif isinstance(pyval, abstract.AsInstance):
            cls = pyval.cls
            if isinstance(cls, pytd.AnythingType):
                return self.create_new_unsolvable(node)
            elif (isinstance(pyval, abstract.AsReturnValue)
                  and isinstance(cls, pytd.NothingType)):
                return self.no_return.to_variable(node)
            var = self.vm.program.NewVariable()
            for t in pytd_utils.UnpackUnion(cls):
                if isinstance(t, pytd.TypeParameter):
                    if not subst or t.name not in subst:
                        raise self.TypeParameterError(t.name)
                    else:
                        for v in subst[t.name].bindings:
                            for source_set in source_sets:
                                var.AddBinding(
                                    self.get_maybe_abstract_instance(v.data)
                                    if discard_concrete_values else v.data,
                                    source_set + [v], node)
                elif isinstance(t, pytd.NothingType):
                    pass
                else:
                    value = self.constant_to_value(abstract.AsInstance(t),
                                                   subst, node)
                    for source_set in source_sets:
                        var.AddBinding(value, source_set, node)
            return var
        elif isinstance(pyval, pytd.Constant):
            return self.constant_to_var(abstract.AsInstance(pyval.type), subst,
                                        node, source_sets,
                                        discard_concrete_values)
        result = self.constant_to_value(pyval, subst, node)
        if result is not None:
            return result.to_variable(node)
        # There might still be bugs on the abstract intepreter when it returns,
        # e.g. a list of values instead of a list of types:
        assert pyval.__class__ != cfg.Variable, pyval
        if pyval.__class__ == tuple:
            # TODO(ampere): This does not allow subclasses. Handle namedtuple
            # correctly.
            # This case needs to go at the end because many things are actually also
            # tuples.
            return self.build_tuple(
                self.vm.root_cfg_node, (self.constant_to_var(
                    v, subst, node, source_sets, discard_concrete_values)
                                        for i, v in enumerate(pyval)))
        raise ValueError("Cannot convert {} to an abstract value".format(
            pyval.__class__))
Exemplo n.º 12
0
 def _convert(self, x, name, as_instance=False):
     pyval = self._parse_and_lookup(x, name)
     if as_instance:
         pyval = abstract.AsInstance(pyval)
     return self.vm.convert.constant_to_value(pyval, {},
                                              self.vm.root_cfg_node)
Exemplo n.º 13
0
  def construct_constant_from_value(self, name, pyval, subst, 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:
      name: The name of this constant. Used for naming its attribute variables.
      pyval: The python or PyTD value to convert.
      subst: The current type parameters.
      node: The current CFG 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.
    """
    if pyval is type:
      return abstract.SimpleAbstractValue(name, self.vm)
    elif isinstance(pyval, str):
      return abstract.AbstractOrConcreteValue(
          pyval, self.str_type, self.vm, node)
    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, node)
    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, node)
    elif pyval.__class__ in [types.FunctionType,
                             types.ModuleType,
                             types.GeneratorType,
                             type]:
      try:
        pyclass = self.vm.vmbuiltins.Lookup("__builtin__." + pyval.__name__)
        return self.convert_constant_to_value(name, pyclass, subst, node)
      except (KeyError, AttributeError):
        log.debug("Failed to find pytd", exc_info=True)
        raise
    elif isinstance(pyval, pytd.TypeDeclUnit):
      data = pyval.constants + pyval.classes + pyval.functions + pyval.aliases
      members = {val.name.rsplit(".")[-1]: val
                 for val in data}
      return abstract.Module(self.vm, node, pyval.name, members)
    elif isinstance(pyval, pytd.Class):
      if "." in pyval.name:
        module, base_name = pyval.name.rsplit(".", 1)
        cls = abstract.PyTDClass(base_name, pyval, self.vm)
        cls.module = module
      else:
        cls = abstract.PyTDClass(name, pyval, self.vm)
      return cls
    elif isinstance(pyval, pytd.Function):
      signatures = [abstract.PyTDSignature(pyval.name, sig, self.vm)
                    for sig in pyval.signatures]
      f = abstract.PyTDFunction(
          pyval.name, signatures, pyval.kind, self.vm, node)
      return f
    elif isinstance(pyval, pytd.ClassType):
      assert pyval.cls
      return self.convert_constant_to_value(pyval.name, pyval.cls, subst, node)
    elif isinstance(pyval, pytd.NothingType):
      return self.nothing
    elif isinstance(pyval, pytd.AnythingType):
      # TODO(kramm): This should be an Unsolveable. We don't need to solve this.
      return self._create_new_unknown_value("AnythingType")
    elif isinstance(pyval, pytd.FunctionType):
      return self.construct_constant_from_value(
          name, pyval.function, subst, node)
    elif isinstance(pyval, pytd.UnionType):
      return abstract.Union([
          self.convert_constant_to_value(pytd.Print(t), t, subst, node)
          for t in pyval.type_list], self.vm)
    elif isinstance(pyval, pytd.TypeParameter):
      return abstract.TypeParameter(pyval.name, self.vm)
    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.convert_constant(cls.name, cls, subst, node)
            instance = abstract.Instance(mycls, self.vm, 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
        instance = abstract.Instance(
            self.convert_constant(base_cls.name, base_cls, subst, node),
            self.vm, node)
        for formal, actual in zip(base_cls.template, cls.parameters):
          p = self.convert_constant(
              repr(formal), abstract.AsInstance(actual), subst, node)
          instance.initialize_type_parameter(node, formal.name, p)
        return instance
      else:
        return self.convert_constant_to_value(name, cls, subst, node)
    elif isinstance(pyval, pytd.GenericType):
      assert isinstance(pyval.base_type, pytd.ClassType)
      type_parameters = utils.LazyDict()
      for param, value in zip(pyval.base_type.cls.template, pyval.parameters):
        type_parameters.add_lazy_item(
            param.name, self.convert_constant_to_value,
            param.name, value, subst, node)
      cls = self.convert_constant_to_value(
          pytd.Print(pyval.base_type), pyval.base_type.cls, subst, node)
      return abstract.ParameterizedClass(cls, type_parameters, self.vm)
    elif pyval.__class__ is tuple:  # only match raw tuple, not namedtuple/Node
      return self.tuple_to_value(self.vm.root_cfg_node,
                                 [self.convert_constant("tuple[%d]" % i, item,
                                                        subst, node)
                                  for i, item in enumerate(pyval)])
    else:
      raise NotImplementedError("Can't convert constant %s %r" %
                                (type(pyval), pyval))
Exemplo 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))