Exemple #1
0
 def pytd_for_types(self, defs):
     data = []
     for name, var in defs.items():
         if name in output.TOP_LEVEL_IGNORE or self._is_builtin(
                 name, var.data):
             continue
         options = var.FilteredData(self.exitpoint)
         if (len(options) > 1 and not all(
                 isinstance(o, (abstract.Function, abstract.BoundFunction))
                 for o in options)):
             # It's ambiguous whether this is a type, a function or something
             # else, so encode it as a constant.
             combined_types = pytd_utils.JoinTypes(
                 t.to_type(self.exitpoint) for t in options)
             data.append(pytd.Constant(name, combined_types))
         elif options:
             for option in options:
                 try:
                     d = option.to_pytd_def(self.exitpoint,
                                            name)  # Deep definition
                 except NotImplementedError:
                     d = option.to_type(self.exitpoint)  # Type only
                     if isinstance(d, pytd.NothingType):
                         assert isinstance(option, abstract.Empty)
                         d = pytd.AnythingType()
                 if isinstance(d, pytd.TYPE) and not isinstance(
                         d, pytd.TypeParameter):
                     data.append(pytd.Constant(name, d))
                 else:
                     data.append(d)
         else:
             log.error("No visible options for " + name)
             data.append(pytd.Constant(name, pytd.AnythingType()))
     return pytd_utils.WrapTypeDeclUnit("inferred", data)
Exemple #2
0
 def pytd_for_types(self, defs, ignore):
   for name, var in defs.items():
     abstract.variable_set_official_name(var, name)
   data = []
   for name, var in defs.items():
     if name in output.TOP_LEVEL_IGNORE or name in ignore:
       continue
     options = var.FilteredData(self.exitpoint)
     if (len(options) > 1 and not
         all(isinstance(o, (abstract.Function, abstract.BoundFunction))
             for o in options)):
       # It's ambiguous whether this is a type, a function or something
       # else, so encode it as a constant.
       combined_types = pytd_utils.JoinTypes(t.to_type(self.exitpoint)
                                             for t in options)
       data.append(pytd.Constant(name, combined_types))
     else:
       for option in options:
         if hasattr(option, "to_pytd_def"):
           d = option.to_pytd_def(self.exitpoint, name)  # Deep definition
         else:
           d = option.to_type(self.exitpoint)  # Type only
         if isinstance(d, pytd.TYPE):
           data.append(pytd.Constant(name, d))
         else:
           data.append(d)
   return pytd_utils.WrapTypeDeclUnit("inferred", data)
Exemple #3
0
  def _new_named_tuple(
      self,
      class_name: str,
      fields: List[Tuple[str, Any]]
  ) -> pytd.Class:
    """Generates a pytd class for a named tuple.

    Args:
      class_name: The name of the generated class
      fields: A list of (name, type) tuples.

    Returns:
      A generated class that describes the named tuple.
    """
    class_parent = types.heterogeneous_tuple(pytd.NamedType("tuple"),
                                             tuple(t for _, t in fields))
    class_constants = tuple(pytd.Constant(n, t) for n, t in fields)
    # Since the user-defined fields are the only namedtuple attributes commonly
    # used, we define all the other attributes as Any for simplicity.
    class_constants += tuple(pytd.Constant(name, pytd.AnythingType())
                             for name in _NAMEDTUPLE_MEMBERS)
    methods = function.merge_method_signatures(
        [self._make_new(class_name, fields), self._make_init()])
    return pytd.Class(name=class_name,
                      metaclass=None,
                      parents=(class_parent,),
                      methods=tuple(methods),
                      constants=class_constants,
                      decorators=(),
                      classes=(),
                      slots=tuple(n for n, _ in fields),
                      template=())
Exemple #4
0
 def new_alias_or_constant(self, name, value):
   """Build an alias or constant."""
   # This is here rather than in _Definitions because we need to build a
   # constant or alias from a partially converted typed_ast subtree.
   if name == "__slots__":
     if not (isinstance(value, ast3.List) and
             all(types.Pyval.is_str(x) for x in value.elts)):
       raise ParseError("__slots__ must be a list of strings")
     return types.SlotDecl(tuple(x.value for x in value.elts))
   elif isinstance(value, types.Pyval):
     return pytd.Constant(name, value.to_pytd())
   elif isinstance(value, types.Ellipsis):
     return pytd.Constant(name, pytd.AnythingType())
   elif isinstance(value, pytd.NamedType):
     res = self.defs.resolve_type(value.name)
     return pytd.Alias(name, res)
   elif isinstance(value, ast3.List):
     if name != "__all__":
       raise ParseError("Only __slots__ and __all__ can be literal lists")
     return pytd.Constant(name, pytdgen.pytd_list("str"))
   elif isinstance(value, ast3.Tuple):
     # TODO(mdemello): Consistent with the current parser, but should it
     # properly be Tuple[Type]?
     return pytd.Constant(name, pytd.NamedType("tuple"))
   elif isinstance(value, ast3.Name):
     value = self.defs.resolve_type(value.id)
     return pytd.Alias(name, value)
   else:
     # TODO(mdemello): add a case for TypeVar()
     # Convert any complex type aliases
     value = self.convert_node(value)
     return pytd.Alias(name, value)
Exemple #5
0
 def _setup_pytdclass(self, node, cls):
     # We need to rewrite the member map of the PytdClass.
     members = dict(cls._member_map)  # pylint: disable=protected-access
     member_types = []
     for name, pytd_val in members.items():
         # Only constants need to be transformed.
         # TODO(tsudol): Ensure only valid enum members are transformed.
         if not isinstance(pytd_val, pytd.Constant):
             continue
         # Build instances directly, because you can't call instantiate() when
         # creating the class -- pytype complains about recursive types.
         member = abstract.Instance(cls, self.vm)
         member.members["name"] = self.vm.convert.constant_to_var(
             pyval=pytd.Constant(name="name", type=self._str_pytd),
             node=node)
         member.members["value"] = self.vm.convert.constant_to_var(
             pyval=pytd.Constant(name="value", type=pytd_val.type),
             node=node)
         cls._member_map[name] = member  # pylint: disable=protected-access
         cls.members[name] = member.to_variable(node)
         member_types.append(pytd_val.type)
     member_type = self.vm.convert.constant_to_value(
         pytd_utils.JoinTypes(member_types))
     cls.members["__new__"] = self._make_new(node, member_type, cls)
     cls.members["__eq__"] = EnumCmpEQ(self.vm).to_variable(node)
     return node
Exemple #6
0
  def new_named_tuple(self, base_name, fields):
    """Return a type for a named tuple (implicitly generates a class).

    Args:
      base_name: The named tuple's name.
      fields: A list of (name, type) tuples.

    Returns:
      A NamedType() for the generated class that describes the named tuple.
    """
    # Handle previously defined NamedTuples with the same name
    prev_list = self._generated_classes[base_name]
    class_name = "namedtuple-%s-%d" % (base_name, len(prev_list))
    class_parent = self._heterogeneous_tuple(pytd.NamedType("tuple"),
                                             tuple(t for _, t in fields))
    class_constants = tuple(pytd.Constant(n, t) for n, t in fields)
    # Since the user-defined fields are the only namedtuple attributes commonly
    # used, we define all the other attributes as Any for simplicity.
    class_constants += tuple(pytd.Constant(name, pytd.AnythingType())
                             for name in self._NAMEDTUPLE_MEMBERS)
    methods = _merge_method_signatures(
        [self._namedtuple_new(class_name, fields), self._namedtuple_init()])
    nt_class = pytd.Class(name=class_name,
                          metaclass=None,
                          parents=(class_parent,),
                          methods=tuple(methods),
                          constants=class_constants,
                          slots=tuple(n for n, _ in fields),
                          template=())

    self._generated_classes[base_name].append(nt_class)
    return pytd.NamedType(nt_class.name)
Exemple #7
0
 def pytd_for_types(self, defs):
   # If a variable is annotated, we'll always output that type.
   annotated_names = set()
   data = []
   pytd_convert = self.convert.pytd_convert
   annots = abstract_utils.get_annotations_dict(defs)
   for name, t in pytd_convert.annotations_to_instance_types(
       self.exitpoint, annots):
     annotated_names.add(name)
     data.append(pytd.Constant(name, t))
   for name, var in defs.items():
     if (name in output.TOP_LEVEL_IGNORE or name in annotated_names or
         self._is_typing_member(name, var)):
       continue
     options = var.FilteredData(self.exitpoint, strict=False)
     if (len(options) > 1 and
         not all(isinstance(o, abstract.FUNCTION_TYPES) for o in options)):
       if all(isinstance(o, (abstract.ParameterizedClass,
                             abstract.TypeParameter,
                             abstract.Union)) for o in options
              ) and self.options.preserve_union_macros:  # type alias
         data.append(pytd_utils.JoinTypes(t.to_pytd_def(self.exitpoint, name)
                                          for t in options))
       else:
         # It's ambiguous whether this is a type, a function or something
         # else, so encode it as a constant.
         combined_types = pytd_utils.JoinTypes(t.to_type(self.exitpoint)
                                               for t in options)
         data.append(pytd.Constant(name, combined_types))
     elif options:
       for option in options:
         try:
           d = option.to_pytd_def(self.exitpoint, name)  # Deep definition
         except NotImplementedError:
           d = option.to_type(self.exitpoint)  # Type only
           if isinstance(d, pytd.NothingType):
             if isinstance(option, abstract.Empty):
               d = pytd.AnythingType()
             else:
               assert isinstance(option, typing_overlay.NoReturn)
         if isinstance(d, pytd.Type) and not isinstance(d, pytd.TypeParameter):
           data.append(pytd.Constant(name, d))
         else:
           data.append(d)
     else:
       log.error("No visible options for %s", name)
       data.append(pytd.Constant(name, pytd.AnythingType()))
   return pytd_utils.WrapTypeDeclUnit("inferred", data)
Exemple #8
0
 def pytd_for_types(self, defs):
     data = []
     pytd_convert = self.convert.pytd_convert
     annots = pytd_convert.get_annotations_dict(defs)
     for name, t in pytd_convert.uninitialized_annotations_to_instance_types(
             self.exitpoint, annots, defs):
         data.append(pytd.Constant(name, t))
     for name, var in defs.items():
         if name in output.TOP_LEVEL_IGNORE or self._is_builtin(
                 name, var.data):
             continue
         options = []
         for value, is_annotation in pytd_convert.get_annotated_values(
                 self.exitpoint, name, var, annots):
             if is_annotation:
                 data.append(pytd.Constant(name, value))
             else:
                 options.append(value)
         if (len(options) > 1 and not all(
                 isinstance(o, (abstract.Function, abstract.BoundFunction))
                 for o in options)):
             # It's ambiguous whether this is a type, a function or something
             # else, so encode it as a constant.
             combined_types = pytd_utils.JoinTypes(
                 t.to_type(self.exitpoint) for t in options)
             data.append(pytd.Constant(name, combined_types))
         elif options:
             for option in options:
                 try:
                     d = option.to_pytd_def(self.exitpoint,
                                            name)  # Deep definition
                 except NotImplementedError:
                     d = option.to_type(self.exitpoint)  # Type only
                     if isinstance(d, pytd.NothingType):
                         if isinstance(option, abstract.Empty):
                             d = pytd.AnythingType()
                         else:
                             assert isinstance(option,
                                               typing_overlay.NoReturn)
                 if isinstance(d, pytd.TYPE) and not isinstance(
                         d, pytd.TypeParameter):
                     data.append(pytd.Constant(name, d))
                 else:
                     data.append(d)
         else:
             log.error("No visible options for %s", name)
             data.append(pytd.Constant(name, pytd.AnythingType()))
     return pytd_utils.WrapTypeDeclUnit("inferred", data)
Exemple #9
0
  def value_to_pytd_def(self, node, v, name):
    """Get a PyTD definition for this object.

    Args:
      node: The node.
      v: The object.
      name: The object name.

    Returns:
      A PyTD definition.
    """
    if isinstance(v, abstract.PyTDFunction):
      return pytd.Function(
          name, tuple(sig.pytd_sig for sig in v.signatures), pytd.METHOD)
    elif isinstance(v, abstract.InterpreterFunction):
      return self._function_to_def(node, v, name)
    elif isinstance(v, abstract.ParameterizedClass):
      return pytd.Alias(name, v.get_instance_type(node))
    elif isinstance(v, abstract.PyTDClass):
      # This happens if a module does e.g. "from x import y as z", i.e., copies
      # something from another module to the local namespace. We *could*
      # reproduce the entire class, but we choose a more dense representation.
      return v.to_type(node)
    elif isinstance(v, abstract.InterpreterClass):
      return self._class_to_def(node, v, name)
    elif isinstance(v, abstract.TypeVariable):
      return pytd.TypeParameter(name, None)
    elif isinstance(v, abstract.Unsolvable):
      return pytd.Constant(name, v.to_type(node))
    else:
      raise NotImplementedError(v.__class__.__name__)
Exemple #10
0
  def _assign(self, node, target, value):
    name = target.id

    # Record and erase TypeVar and ParamSpec definitions.
    if isinstance(value, _TypeVar):
      self.defs.add_type_var(name, value)
      return Splice([])
    elif isinstance(value, _ParamSpec):
      self.defs.add_param_spec(name, value)
      return Splice([])

    if node.type_comment:
      # TODO(mdemello): can pyi files have aliases with typecomments?
      ret = pytd.Constant(name, node.type_comment)
    else:
      ret = self.new_alias_or_constant(name, value)

    if self.in_function:
      # Should never happen, but this keeps pytype happy.
      if isinstance(ret, types.SlotDecl):
        raise ParseError("Cannot change the type of __slots__")
      return function.Mutator(name, ret.type)

    if self.level == 0:
      self.defs.add_alias_or_constant(ret)
    return ret
Exemple #11
0
    def visit_Assign(self, node):
        targets = node.targets
        if len(targets) > 1 or isinstance(targets[0], ast3.Tuple):
            msg = "Assignments must be of the form 'name = value'"
            raise ParseError(msg)
        self.convert_node_annotations(node)
        target = targets[0]
        name = target.id

        # Record and erase typevar definitions.
        if isinstance(node.value, _TypeVar):
            self.defs.add_type_var(name, node.value)
            return Splice([])

        if node.type_comment:
            # TODO(mdemello): can pyi files have aliases with typecomments?
            ret = pytd.Constant(name, node.type_comment)
        else:
            ret = self.new_alias_or_constant(name, node.value)

        if self.in_function:
            # Should never happen, but this keeps pytype happy.
            if isinstance(ret, types.SlotDecl):
                raise ParseError("Cannot change the type of __slots__")
            return function.Mutator(name, ret.type)

        if self.level == 0:
            self.defs.add_alias_or_constant(ret)
        return ret
Exemple #12
0
  def new_constant(self, name, value):
    """Return a Constant.

    Args:
      name: The name of the constant.
      value: None, 0, or a  pytd type.

    Returns:
      A Constant object.

    Raises:
      ParseError: if value is an int other than 0.
    """
    if value is None:
      t = pytd.AnythingType()
    elif isinstance(value, int):
      if value != 0:
        raise ParseError("Only '0' allowed as int literal")
      t = pytd.NamedType("int")
    elif isinstance(value, float):
      if value != 0.0:
        raise ParseError("Only '0.0' allowed as float literal")
      t = pytd.NamedType("float")
    else:
      t = value
    return pytd.Constant(name, t)
Exemple #13
0
 def VisitClass(self, node):
   constants = list(node.constants)
   for fn in self.const_properties:
     types = [x.return_type for x in fn.signatures]
     constants.append(pytd.Constant(name=fn.name, type=join_types(types)))
   methods = [x for x in node.methods if x not in self.const_properties]
   return node.Replace(constants=tuple(constants), methods=tuple(methods))
Exemple #14
0
    def new_named_tuple(self, base_name, fields):
        """Return a type for a named tuple (implicitly generates a class).

    Args:
      base_name: The named tuple's name.
      fields: A list of (name, type) tuples.

    Returns:
      A NamedType() for the generated class that describes the named tuple.
    """
        # Handle previously defined NamedTuples with the same name
        prev_list = self._generated_classes[base_name]
        name_dedup = "~%d" % len(prev_list) if prev_list else ""
        class_name = "`%s%s`" % (base_name, name_dedup)
        class_parent = self._heterogeneous_tuple(pytd.NamedType("tuple"),
                                                 tuple(t for _, t in fields))
        class_constants = tuple(pytd.Constant(n, t) for n, t in fields)
        nt_class = pytd.Class(name=class_name,
                              metaclass=None,
                              parents=(class_parent, ),
                              methods=(),
                              constants=class_constants,
                              template=())

        self._generated_classes[base_name].append(nt_class)
        return pytd.NamedType(nt_class.name)
Exemple #15
0
    def to_structural_def(self, node, class_name):
        """Convert this Unknown to a pytd.Class."""
        self_param = (pytd.Parameter("self", pytd.AnythingType(),
                                     pytd.ParameterKind.REGULAR, False,
                                     None), )
        starargs = None
        starstarargs = None

        def _make_sig(args, ret):
            return pytd.Signature(self_param + self._make_params(node, args),
                                  starargs,
                                  starstarargs,
                                  return_type=Unknown._to_pytd(node, ret),
                                  exceptions=(),
                                  template=())

        calls = tuple(
            pytd_utils.OrderedSet(
                _make_sig(args, ret) for args, _, ret in self._calls))
        if calls:
            methods = (pytd.Function("__call__", calls,
                                     pytd.MethodKind.METHOD), )
        else:
            methods = ()
        return pytd.Class(name=class_name,
                          metaclass=None,
                          bases=(pytd.NamedType("builtins.object"), ),
                          methods=methods,
                          constants=tuple(
                              pytd.Constant(name, Unknown._to_pytd(node, c))
                              for name, c in self.members.items()),
                          classes=(),
                          decorators=(),
                          slots=None,
                          template=())
Exemple #16
0
def WrapTypeDeclUnit(name, items):
    """Given a list (classes, functions, etc.), wrap a pytd around them.

  Args:
    name: The name attribute of the resulting TypeDeclUnit.
    items: A list of items. Can contain pytd.Class, pytd.Function and
      pytd.Constant.
  Returns:
    A pytd.TypeDeclUnit.
  Raises:
    ValueError: In case of an invalid item in the list.
    NameError: For name conflicts.
  """

    functions = collections.OrderedDict()
    classes = collections.OrderedDict()
    constants = collections.defaultdict(TypeBuilder)
    aliases = collections.OrderedDict()
    for item in items:
        if isinstance(item, pytd.Function):
            if item.name in functions:
                if item.kind != functions[item.name].kind:
                    raise ValueError("Can't combine %s and %s", item.kind,
                                     functions[item.name].kind)
                functions[item.name] = pytd.Function(
                    item.name,
                    functions[item.name].signatures + item.signatures,
                    item.kind)
            else:
                functions[item.name] = item
        elif isinstance(item, pytd.Class):
            if item.name in classes:
                raise NameError("Duplicate top level class: %r", item.name)
            classes[item.name] = item
        elif isinstance(item, pytd.Constant):
            constants[item.name].add_type(item.type)
        elif isinstance(item, pytd.Alias):
            if item.name in aliases:
                raise NameError("Duplicate top level alias or import: %r",
                                item.name)
            aliases[item.name] = item
        else:
            raise ValueError("Invalid top level pytd item: %r" % type(item))

    _check_intersection(functions, classes, "function", "class")
    _check_intersection(functions, constants, "functions", "constant")
    _check_intersection(functions, aliases, "functions", "aliases")
    _check_intersection(classes, constants, "class", "constant")
    _check_intersection(classes, aliases, "class", "alias")
    _check_intersection(constants, aliases, "constant", "alias")

    return pytd.TypeDeclUnit(name=name,
                             constants=tuple(
                                 pytd.Constant(name, t.build())
                                 for name, t in sorted(constants.items())),
                             type_params=tuple(),
                             classes=tuple(classes.values()),
                             functions=tuple(functions.values()),
                             aliases=tuple(aliases.values()))
Exemple #17
0
 def visit_AnnAssign(self, node):
     name = node.target.id
     typ = node.annotation
     val = self.convert_node(node.value)
     if val and not types.is_any(val):
         msg = f"Default value for {name}: {typ.name} can only be '...', got {val}"
         raise ParseError(msg)
     return pytd.Constant(name, typ, val)
Exemple #18
0
 def new_alias_or_constant(self, name_and_value):
   name, value = name_and_value
   if name == "__slots__":
     return _SlotDecl(value)
   elif value in [pytd.NamedType("True"), pytd.NamedType("False")]:
     return pytd.Constant(name, pytd.NamedType("bool"))
   else:
     return pytd.Alias(name, value)
Exemple #19
0
    def value_to_pytd_def(self, node, v, name):
        """Get a PyTD definition for this object.

    Args:
      node: The node.
      v: The object.
      name: The object name.

    Returns:
      A PyTD definition.
    """
        if isinstance(v, abstract.Module):
            return pytd.Alias(name, pytd.Module(name, module_name=v.full_name))
        elif isinstance(v, abstract.BoundFunction):
            d = self.value_to_pytd_def(node, v.underlying, name)
            assert isinstance(d, pytd.Function)
            sigs = tuple(
                sig.Replace(params=sig.params[1:]) for sig in d.signatures)
            return d.Replace(signatures=sigs)
        elif isinstance(v, attr_overlay.Attrs):
            ret = pytd.NamedType("typing.Callable")
            md = metadata.to_pytd(v.to_metadata())
            return pytd.Annotated(ret, ("'pytype_metadata'", md))
        elif (isinstance(v, abstract.PyTDFunction)
              and not isinstance(v, typing_overlay.TypeVar)):
            return pytd.Function(
                name=name,
                signatures=tuple(sig.pytd_sig for sig in v.signatures),
                kind=v.kind,
                flags=pytd.MethodFlag.abstract_flag(v.is_abstract))
        elif isinstance(v, abstract.InterpreterFunction):
            return self._function_to_def(node, v, name)
        elif isinstance(v, abstract.SimpleFunction):
            return self._simple_func_to_def(node, v, name)
        elif isinstance(v, (abstract.ParameterizedClass, abstract.Union)):
            return pytd.Alias(name, v.get_instance_type(node))
        elif isinstance(v, abstract.PyTDClass) and v.module:
            # This happens if a module does e.g. "from x import y as z", i.e., copies
            # something from another module to the local namespace. We *could*
            # reproduce the entire class, but we choose a more dense representation.
            return v.to_type(node)
        elif isinstance(v, typed_dict.TypedDictClass):
            return self._typed_dict_to_def(node, v, name)
        elif isinstance(v, abstract.PyTDClass):  # a namedtuple instance
            assert name != v.name
            return pytd.Alias(name, pytd.NamedType(v.name))
        elif isinstance(v, abstract.InterpreterClass):
            if v.official_name is None or name == v.official_name:
                return self._class_to_def(node, v, name)
            else:
                return pytd.Alias(name, pytd.NamedType(v.official_name))
        elif isinstance(v, abstract.TypeParameter):
            return self._typeparam_to_def(node, v, name)
        elif isinstance(v, abstract.Unsolvable):
            return pytd.Constant(name, v.to_type(node))
        else:
            raise NotImplementedError(v.__class__.__name__)
Exemple #20
0
 def VisitClass(self, node):
   constants = list(node.constants)
   for fn in self.const_properties[-1]:
     ptypes = [x.return_type for x in fn.signatures]
     prop = pytd.Annotated(base_type=pytd_utils.JoinTypes(ptypes),
                           annotations=("'property'",))
     constants.append(pytd.Constant(name=fn.name, type=prop))
   methods = [x for x in node.methods if x not in self.const_properties[-1]]
   return node.Replace(constants=tuple(constants), methods=tuple(methods))
Exemple #21
0
def builtin_keyword_constants():
  # We cannot define these in a pytd file because assigning to a keyword breaks
  # the python parser.
  defs = [
      ("True", "bool"),
      ("False", "bool"),
      ("None", "NoneType"),
      ("__debug__", "bool")
  ]
  return [pytd.Constant(name, pytd.NamedType(typ)) for name, typ in defs]
Exemple #22
0
  def testPrintImportsNamedType(self):
    # Can't get tree by parsing so build explicitly
    node = pytd.Constant("x", pytd.NamedType("typing.List"))
    tree = pytd_utils.CreateModule(name=None, constants=(node,))
    expected_src = textwrap.dedent("""
      from typing import List

      x: List
    """).strip()
    res = pytd.Print(tree)
    self.assertMultiLineEqual(res, expected_src)
Exemple #23
0
def _maybe_resolve_alias(alias, name_to_class, name_to_constant):
    """Resolve the alias if possible.

  Args:
    alias: A pytd.Alias
    name_to_class: A class map used for resolution.
    name_to_constant: A constant map used for resolution.

  Returns:
    None, if the alias pointed to an un-aliasable type.
    The resolved value, if the alias was resolved.
    The alias, if it was not resolved.
  """
    if not isinstance(alias.type, pytd.NamedType):
        return alias
    if alias.type.name in _TYPING_SETS:
        # Filter out aliases to `typing` members that don't appear in typing.pytd
        # to avoid lookup errors.
        return None
    if "." not in alias.type.name:
        # We'll handle nested classes specially, since they need to be represented
        # as constants to distinguish them from imports.
        return alias
    parts = alias.type.name.split(".")
    if parts[0] not in name_to_class and parts[0] not in name_to_constant:
        return alias
    prev_value = None
    value = name_to_class.get(parts[0]) or name_to_constant[parts[0]]
    for part in parts[1:]:
        prev_value = value
        # We can immediately return upon encountering an error, as load_pytd will
        # complain when it can't resolve the alias.
        if isinstance(value, pytd.Constant):
            if (not isinstance(value.type, pytd.NamedType)
                    or value.type.name not in name_to_class):
                # TODO(rechen): Parameterized constants of generic classes should
                # probably also be allowed.
                return alias
            value = name_to_class[value.type.name]
        if not isinstance(value, pytd.Class):
            return alias
        try:
            value = value.Lookup(part)
        except KeyError:
            return alias
    if isinstance(value, pytd.Class):
        return pytd.Constant(
            alias.name, pytdgen.pytd_type(pytd.NamedType(alias.type.name)))
    elif isinstance(value, pytd.Function):
        return pytd_utils.AliasMethod(value.Replace(name=alias.name),
                                      from_constant=isinstance(
                                          prev_value, pytd.Constant))
    else:
        return value.Replace(name=alias.name)
Exemple #24
0
def _merge_property_signatures(signatures):
  name_to_property_types = collections.OrderedDict()
  for signature in signatures:
    if signature.name not in name_to_property_types:
      name_to_property_types[signature.name] = []
    property_type = _parse_signature_as_property(signature)
    if property_type:
      name_to_property_types[signature.name].append(property_type)
  return [
      pytd.Constant(
          name=name, type=join_types(types) if types else pytd.AnythingType())
      for name, types in name_to_property_types.items()]
Exemple #25
0
 def testLookupConstant(self):
   src1 = textwrap.dedent("""
     Foo = ...  # type: type
   """)
   src2 = textwrap.dedent("""
     class Bar(object):
       bar = ...  # type: foo.Foo
   """)
   ast1 = self.Parse(src1, name="foo")
   ast2 = self.Parse(src2, name="bar")
   ast2 = ast2.Visit(visitors.LookupExternalTypes({"foo": ast1, "bar": ast2}))
   self.assertEqual(ast2.Lookup("bar.Bar").constants[0],
                    pytd.Constant(name="bar", type=pytd.AnythingType()))
Exemple #26
0
 def test_basic(self):
   src = textwrap.dedent("""
     if sys.version_info[:2] >= (3, 9) and sys.platform == 'linux':
       a: int
     elif sys.version_info[0] == 3:
       a: bool
     else:
       a: str
   """)
   root = ast_parser.parse_pyi(src, "foo.py", "foo", (3, 6))
   self.assertCountEqual(root.constants, (
       pytd.Constant("foo.a", pytd.NamedType("bool")),
   ))
  def testPrintImportsNamedType(self):
    # Can't get tree by parsing so build explicitly
    node = pytd.Constant("x", pytd.NamedType("typing.List"))
    tree = pytd.TypeDeclUnit(constants=(node,), type_params=(),
                             functions=(), classes=(), aliases=(), name=None)

    expected_src = textwrap.dedent("""
      import typing

      x = ...  # type: typing.List
    """).strip()
    res = pytd.Print(tree)
    self.assertMultiLineEqual(res, expected_src)
Exemple #28
0
  def _class_to_def(self, node, v, class_name):
    """Convert an InterpreterClass to a PyTD definition."""
    methods = {}
    constants = collections.defaultdict(pytd_utils.TypeBuilder)

    # class-level attributes
    for name, member in v.members.items():
      if name not in CLASS_LEVEL_IGNORE:
        for value in member.FilteredData(v.vm.exitpoint):
          if isinstance(value, abstract.Function):
            val = self.value_to_pytd_def(node, value, name)
            if isinstance(val, pytd.Function):
              methods[name] = val
            elif isinstance(v, pytd.TYPE):
              constants[name].add_type(val)
            else:
              raise AssertionError(str(type(val)))
          else:
            constants[name].add_type(value.to_type(node))

    # instance-level attributes
    for instance in v.instances:
      for name, member in instance.members.items():
        if name not in CLASS_LEVEL_IGNORE:
          for value in member.FilteredData(v.vm.exitpoint):
            constants[name].add_type(value.to_type(node))

    for name in list(methods):
      if name in constants:
        # If something is both a constant and a method, it means that the class
        # is, at some point, overwriting its own methods with an attribute.
        del methods[name]
        constants[name].add_type(pytd.AnythingType())

    bases = [pytd_utils.JoinTypes(b.get_instance_type(node)
                                  for b in basevar.data)
             for basevar in v.bases()
             if basevar is not v.vm.convert.oldstyleclass_type]
    constants = [pytd.Constant(name, builder.build())
                 for name, builder in constants.items() if builder]
    metaclass = v.metaclass(node)
    if metaclass is not None:
      metaclass = metaclass.get_instance_type(node)
    return pytd.Class(name=class_name,
                      metaclass=metaclass,
                      parents=tuple(bases),
                      methods=tuple(methods.values()),
                      constants=tuple(constants),
                      template=())
Exemple #29
0
 def _typed_dict_to_def(self, node, v, name):
     constants = []
     for k, var in v.props.fields.items():
         typ = pytd_utils.JoinTypes(
             self.value_instance_to_pytd_type(node, p, None, set(), {})
             for p in var.data)
         constants.append(pytd.Constant(k, typ))
     bases = (pytd.NamedType("typing.TypedDict"), )
     return pytd.Class(name=name,
                       metaclass=None,
                       bases=bases,
                       methods=(),
                       constants=tuple(constants),
                       classes=(),
                       decorators=(),
                       slots=None,
                       template=())
Exemple #30
0
    def add_alias_or_constant(self, name, value):
        """Add an alias or constant.

    Args:
      name: The name of the alias or constant.
      value: A pytd type.  If the type is NamedType("True") or
          NamedType("False") the name becomes a constant of type bool,
          otherwise it becomes an alias.
    """
        if not self._current_condition.active:
            return
        # TODO(dbaum): Consider merging this with new_constant().
        if value in [pytd.NamedType("True"), pytd.NamedType("False")]:
            self._constants.append(pytd.Constant(name, pytd.NamedType("bool")))
        else:
            self._type_map[name] = value
            self._aliases.append(pytd.Alias(name, value))