示例#1
0
 def testUnionEquality(self):
     union1 = abstract.Union((self._vm.convert.unsolvable, ), self._vm)
     union2 = abstract.Union((self._vm.convert.none, ), self._vm)
     cls = abstract.InterpreterClass("Union", [], {}, None, self._vm)
     self.assertEqual(union1, union1)
     self.assertNotEqual(union1, union2)
     self.assertNotEqual(union1, cls)
示例#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_utils.AsInstance(x), {}, self._vm.root_cfg_node)
   self.assertIsInstance(cls, abstract.CallableClass)
   six.assertCountEqual(
       self,
       cls.formal_type_parameters.items(),
       [(0, self._vm.convert.int_type),
        (1, self._vm.convert.primitive_classes[bool]),
        (abstract_utils.ARGS, abstract.Union(
            [cls.formal_type_parameters[0], cls.formal_type_parameters[1]],
            self._vm)),
        (abstract_utils.RET, self._vm.convert.str_type)])
   self.assertIsInstance(instance, abstract.Instance)
   self.assertEqual(instance.cls, cls)
   six.assertCountEqual(
       self,
       [(name, set(var.data))
        for name, var in instance.instance_type_parameters.items()],
       [(abstract_utils.full_type_name(instance, abstract_utils.ARGS),
         {self._vm.convert.primitive_class_instances[int],
          self._vm.convert.primitive_class_instances[bool]}),
        (abstract_utils.full_type_name(instance, abstract_utils.RET),
         {self._vm.convert.primitive_class_instances[str]})])
示例#3
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]
     ])
示例#4
0
 def optionalize(self, value):
     """Optionalize the value, if necessary."""
     assert isinstance(value, abstract.AtomicAbstractValue)
     none_type = self.vm.convert.none_type
     if isinstance(value, abstract.Union) and none_type in value.options:
         return value
     return abstract.Union((value, none_type), self.vm)
示例#5
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, {}, self._vm.root_cfg_node)
   instance = self._vm.convert.constant_to_value(
       abstract_utils.AsInstance(x), {}, self._vm.root_cfg_node)
   self.assertIsInstance(cls, abstract.TupleClass)
   six.assertCountEqual(self, cls.formal_type_parameters.items(),
                        [(0, self._vm.convert.str_type),
                         (1, self._vm.convert.int_type),
                         (abstract_utils.T, abstract.Union([
                             cls.formal_type_parameters[0],
                             cls.formal_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]]])
   # The order of option elements in Union is random
   six.assertCountEqual(
       self, instance.get_instance_type_parameter(abstract_utils.T).data,
       [self._vm.convert.primitive_class_instances[str],
        self._vm.convert.primitive_class_instances[int]])
示例#6
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})
示例#7
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])
示例#8
0
 def test_metaclass_union(self):
   cls = abstract.InterpreterClass("X", [], {}, None, self._vm)
   meta1 = abstract.InterpreterClass("M1", [], {}, None, self._vm)
   meta2 = abstract.InterpreterClass("M2", [], {}, None, self._vm)
   meta1.official_name = "M1"
   meta2.official_name = "M2"
   cls.cls = abstract.Union([meta1, meta2], self._vm)
   pytd_cls = cls.to_pytd_def(self._vm.root_cfg_node, "X")
   self.assertEqual(pytd_cls.metaclass, pytd.UnionType(
       (pytd.NamedType("M1"), pytd.NamedType("M2"))))
示例#9
0
 def test_union_set_attribute(self):
     list_instance = abstract.Instance(self._vm.convert.list_type, self._vm)
     cls = abstract.InterpreterClass("obj", [], {}, None, self._vm)
     cls_instance = abstract.Instance(cls, self._vm)
     union = abstract.Union([cls_instance, list_instance], self._vm)
     node = self._vm.attribute_handler.set_attribute(
         self._vm.root_cfg_node, union, "rumpelstiltskin",
         self._vm.convert.none_type.to_variable(self._vm.root_cfg_node))
     self.assertEqual(cls_instance.members["rumpelstiltskin"].data.pop(),
                      self._vm.convert.none_type)
     self.assertIs(node, self._vm.root_cfg_node)
     error, = self._vm.errorlog.unique_sorted_errors()
     self.assertEqual(error.name, "not-writable")
示例#10
0
 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
示例#11
0
 def test_signature_from_callable(self):
   # Callable[[int, str], Any]
   params = {0: self._vm.convert.int_type, 1: self._vm.convert.str_type}
   params[abstract_utils.ARGS] = abstract.Union(
       (params[0], params[1]), self._vm)
   params[abstract_utils.RET] = self._vm.convert.unsolvable
   callable_val = abstract.CallableClass(
       self._vm.convert.function_type, params, self._vm)
   sig = function.Signature.from_callable(callable_val)
   self.assertEqual(repr(sig), "def <callable>(_0: int, _1: str) -> Any")
   self.assertEqual(sig.name, "<callable>")
   self.assertSequenceEqual(sig.param_names, ("_0", "_1"))
   self.assertIs(sig.varargs_name, None)
   self.assertFalse(sig.kwonly_params)
   self.assertIs(sig.kwargs_name, None)
   six.assertCountEqual(self, sig.annotations.keys(), sig.param_names)
   self.assertFalse(sig.has_return_annotation)
   self.assertTrue(sig.has_param_annotations)
示例#12
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))
示例#13
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))
示例#14
0
 def _build_value(self, node, inner, _):
     return abstract.Union(self.options + inner, self.vm)
示例#15
0
 def testUnion(self):
     left_option1 = self._make_class("o1")
     left_option2 = self._make_class("o2")
     left = abstract.Union([left_option1, left_option2], self.vm)
     self.assertMatch(left, self.type_type)
示例#16
0
 def testClassAgainstTypeUnion(self):
     left = self._make_class("foo")
     union = abstract.Union((left, ), self.vm)
     right = abstract.ParameterizedClass(self.type_type,
                                         {abstract.T: union}, self.vm)
     self.assertMatch(left, right)
示例#17
0
 def _build_value(self, node, inner, ellipses):
     self.vm.errorlog.invalid_ellipses(self.vm.frames, ellipses, self.name)
     if len(inner) != 1:
         error = "typing.Optional can only contain one type parameter"
         self.vm.errorlog.invalid_annotation(self.vm.frames, self, error)
     return abstract.Union((self.vm.convert.none_type, ) + inner, self.vm)
示例#18
0
 def _build_value(self, node, inner, ellipses):
     self.vm.errorlog.invalid_ellipses(self.vm.frames, ellipses, self.name)
     return abstract.Union(self.options + inner, self.vm)
示例#19
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
示例#20
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
示例#21
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))