Esempio n. 1
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_base = pytdgen.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,
                          bases=(class_base, ),
                          methods=tuple(methods),
                          constants=class_constants,
                          decorators=(),
                          classes=(),
                          slots=tuple(n for n, _ in fields),
                          template=())
Esempio n. 2
0
    def build_type_decl_unit(self, defs) -> pytd.TypeDeclUnit:
        """Return a pytd.TypeDeclUnit for the given defs (plus parser state)."""
        # defs contains both constant and function definitions.
        constants, functions, aliases, slots, classes = _split_definitions(
            defs)
        assert not slots  # slots aren't allowed on the module level

        # TODO(mdemello): alias/constant handling is broken in some weird manner.
        # assert not aliases # We handle top-level aliases in add_alias_or_constant
        # constants.extend(self.constants)

        if self.module_info.module_name == "builtins":
            constants.extend(types.builtin_keyword_constants())

        generated_classes = sum(self.generated_classes.values(), [])

        classes = generated_classes + classes
        functions = function.merge_method_signatures(functions)

        name_to_class = {c.name: c for c in classes}
        name_to_constant = {c.name: c for c in constants}
        aliases = []
        for a in self.aliases.values():
            t = _maybe_resolve_alias(a, name_to_class, name_to_constant)
            if t is None:
                continue
            elif isinstance(t, pytd.Function):
                functions.append(t)
            elif isinstance(t, pytd.Constant):
                constants.append(t)
            else:
                assert isinstance(t, pytd.Alias)
                aliases.append(t)

        all_names = ([f.name for f in functions] + [c.name
                                                    for c in constants] +
                     [c.name for c in self.type_params] +
                     [c.name for c in classes] + [c.name for c in aliases])
        duplicates = [
            name for name, count in collections.Counter(all_names).items()
            if count >= 2
        ]
        if duplicates:
            raise ParseError("Duplicate top-level identifier(s): " +
                             ", ".join(duplicates))

        properties = [x for x in functions if x.kind == pytd.PROPERTY]
        if properties:
            prop_names = ", ".join(p.name for p in properties)
            raise ParseError(
                "Module-level functions with property decorators: " +
                prop_names)

        return pytd.TypeDeclUnit(name=None,
                                 constants=tuple(constants),
                                 type_params=tuple(self.type_params),
                                 functions=tuple(functions),
                                 classes=tuple(classes),
                                 aliases=tuple(aliases))
Esempio n. 3
0
 def new_new_type(self, name, typ):
     """Returns a type for a NewType."""
     args = [("self", pytd.AnythingType()), ("val", typ)]
     ret = pytd.NamedType("NoneType")
     methods = function.merge_method_signatures(
         [function.NameAndSig.make("__init__", args, ret)])
     cls_name = escape.pack_newtype_base_class(
         name, len(self.generated_classes[name]))
     cls = pytd.Class(name=cls_name,
                      metaclass=None,
                      parents=(typ, ),
                      methods=tuple(methods),
                      constants=(),
                      decorators=(),
                      classes=(),
                      slots=None,
                      template=())
     self.generated_classes[name].append(cls)
     return pytd.NamedType(cls_name)
Esempio n. 4
0
    def build_class(self, class_name, bases, keywords, decorators,
                    defs) -> pytd.Class:
        """Build a pytd.Class from definitions collected from an ast node."""
        parents, namedtuple_index = classdef.get_parents(bases)
        metaclass = classdef.get_metaclass(keywords, parents)
        constants, methods, aliases, slots, classes = _split_definitions(defs)

        # Make sure we don't have duplicate definitions.
        classdef.check_for_duplicate_defs(methods, constants, aliases)

        # Generate a NamedTuple proxy base class if needed
        if namedtuple_index is not None:
            namedtuple_parent = self.new_named_tuple(class_name,
                                                     [(c.name, c.type)
                                                      for c in constants])
            parents[namedtuple_index] = namedtuple_parent
            constants = []

        if aliases:
            vals_dict = {
                val.name: val
                for val in constants + aliases + methods + classes
            }
            for val in aliases:
                name = val.name
                seen_names = set()
                while isinstance(val, pytd.Alias):
                    if isinstance(val.type, pytd.NamedType):
                        _, _, base_name = val.type.name.rpartition(".")
                        if base_name in seen_names:
                            # This happens in cases like:
                            # class X:
                            #   Y = something.Y
                            # Since we try to resolve aliases immediately, we don't know what
                            # type to fill in when the alias value comes from outside the
                            # class. The best we can do is Any.
                            val = pytd.Constant(name, pytd.AnythingType())
                            continue
                        seen_names.add(base_name)
                        if base_name in vals_dict:
                            val = vals_dict[base_name]
                            continue
                    # The alias value comes from outside the class. The best we can do is
                    # to fill in Any.
                    val = pytd.Constant(name, pytd.AnythingType())
                if isinstance(val, function.NameAndSig):
                    val = dataclasses.replace(val, name=name)
                    methods.append(val)
                else:
                    if isinstance(val, pytd.Class):
                        t = pytdgen.pytd_type(
                            pytd.NamedType(class_name + "." + val.name))
                    else:
                        t = val.type
                    constants.append(pytd.Constant(name, t))

        parents = [p for p in parents if not isinstance(p, pytd.NothingType)]
        methods = function.merge_method_signatures(methods)
        if not parents and class_name not in ["classobj", "object"]:
            # A parent-less class inherits from classobj in Python 2 and from object
            # in Python 3. typeshed assumes the Python 3 behavior for all stubs, so we
            # do the same here.
            parents = (pytd.NamedType("object"), )

        return pytd.Class(name=class_name,
                          metaclass=metaclass,
                          parents=tuple(parents),
                          methods=tuple(methods),
                          constants=tuple(constants),
                          classes=tuple(classes),
                          decorators=tuple(decorators),
                          slots=slots,
                          template=())
Esempio n. 5
0
def make_dataclass(cls: pytd.Class) -> pytd.Class:
    _check_defaults(cls)
    init = _make_init(cls)
    methods = cls.methods + tuple(function.merge_method_signatures([init]))
    return cls.Replace(methods=methods)