示例#1
0
  def decorate(self, node, cls):
    """Processes the attrib members of a class."""
    # Collect classvars to convert them to attrs.
    if self.args[cls]["auto_attribs"]:
      ordering = classgen.Ordering.FIRST_ANNOTATE
    else:
      ordering = classgen.Ordering.LAST_ASSIGN
    ordered_locals = classgen.get_class_locals(
        cls.name, allow_methods=False, ordering=ordering, vm=self.vm)
    own_attrs = []
    for name, local in ordered_locals.items():
      typ, orig = local.get_type(node, name), local.orig
      if is_attrib(orig):
        attrib = orig.data[0]
        if typ and attrib.has_type:
          # We cannot have both a type annotation and a type argument.
          self.vm.errorlog.invalid_annotation(self.vm.frames, typ)
          attr = Attribute(
              name=name,
              typ=self.vm.convert.unsolvable,
              init=attrib.init,
              kw_only=attrib.kw_only,
              default=attrib.default)
        elif not typ:
          # Replace the attrib in the class dict with its type.
          attr = Attribute(
              name=name,
              typ=attrib.typ,
              init=attrib.init,
              kw_only=attrib.kw_only,
              default=attrib.default)
          cls.members[name] = classgen.instantiate(node, name, attr.typ)
        else:
          # cls.members[name] has already been set via a typecomment
          attr = Attribute(
              name=name,
              typ=typ,
              init=attrib.init,
              kw_only=attrib.kw_only,
              default=attrib.default)
        self.vm.check_annotation_type_mismatch(
            node, attr.name, attr.typ, attr.default, local.stack,
            allow_none=True)
        own_attrs.append(attr)
      elif self.args[cls]["auto_attribs"]:
        if not match_classvar(typ):
          self.vm.check_annotation_type_mismatch(
              node, name, typ, orig, local.stack, allow_none=True)
          attr = Attribute(
              name=name, typ=typ, init=True, kw_only=False, default=orig)
          if not orig:
            cls.members[name] = classgen.instantiate(node, name, typ)
          own_attrs.append(attr)

    attrs = cls.compute_attr_metadata(own_attrs, "attr.s")

    # Add an __init__ method
    if self.args[cls]["init"]:
      init_method = self.make_init(node, cls, attrs)
      cls.members["__init__"] = init_method
示例#2
0
    def decorate(self, node, cls):
        """Processes class members."""

        # Collect classvars to convert them to attrs. @dataclass collects vars with
        # an explicit type annotation, in order of annotation, so that e.g.
        # class A:
        #   x: int
        #   y: str = 'hello'
        #   x = 10
        # would have init(x:int = 10, y:str = 'hello')
        own_attrs = []
        cls_locals = self.get_class_locals(node, cls)
        for name, local in cls_locals.items():
            typ, orig = local.get_type(node, name), local.orig
            assert typ
            if match_classvar(typ):
                continue
            initvar_typ = self._handle_initvar(node, cls, name, typ, orig)
            if initvar_typ:
                typ = initvar_typ
                init = True
            else:
                if not orig:
                    cls.members[name] = classgen.instantiate(node, name, typ)
                if is_field(orig):
                    field = orig.data[0]
                    orig = field.default
                    init = field.init
                else:
                    init = True

            # TODO(b/74434237): The first check can be removed once
            # --check-variable-types is on by default.
            if ((not self.vm.options.check_variable_types and local.last_op
                 and local.last_op.line in self.vm.director.type_comments)
                    or orig and orig.data == [self.vm.convert.none]):
                # vm._apply_annotation mostly takes care of checking that the default
                # matches the declared type. However, it allows None defaults, and
                # dataclasses do not.
                self.vm.check_annotation_type_mismatch(node,
                                                       name,
                                                       typ,
                                                       orig,
                                                       local.stack,
                                                       allow_none=False)

            attr = classgen.Attribute(name=name,
                                      typ=typ,
                                      init=init,
                                      kw_only=False,
                                      default=orig)
            own_attrs.append(attr)

        attrs = cls.compute_attr_metadata(own_attrs, "dataclasses.dataclass")

        # Add an __init__ method if one doesn't exist already (dataclasses do not
        # overwrite an explicit __init__ method).
        if "__init__" not in cls.members and self.args[cls]["init"]:
            init_method = self.make_init(node, cls, attrs)
            cls.members["__init__"] = init_method
示例#3
0
 def _handle_initvar(self, node, cls, name, typ, orig):
     """Unpack or delete an initvar in the class annotations."""
     initvar = match_initvar(typ)
     if not initvar:
         return None
     # The InitVar annotation is not retained as a class member, but any default
     # value is retained.
     del self.vm.annotated_locals[cls.name][name]
     if orig is not None:
         cls.members[name] = classgen.instantiate(node, name, initvar)
     return initvar
 def _handle_initvar(self, node, cls, name, typ, orig):
     """Unpack or delete an initvar in the class annotations."""
     initvar = match_initvar(typ)
     if not initvar:
         return None
     # The InitVar annotation is not retained as a class member, but any default
     # value is retained.
     if orig is None:
         # If an initvar does not have a default, it will not be a class member
         # variable, so delete it from the annotated locals. Otherwise, leave the
         # annotation as InitVar[...].
         del self.vm.annotated_locals[cls.name][name]
     else:
         cls.members[name] = classgen.instantiate(node, name, initvar)
     return initvar
示例#5
0
    def decorate(self, node, cls):
        """Processes class members."""

        # Collect classvars to convert them to attrs. @dataclass collects vars with
        # an explicit type annotation, in order of annotation, so that e.g.
        # class A:
        #   x: int
        #   y: str = 'hello'
        #   x = 10
        # would have init(x:int = 10, y:str = 'hello')
        own_attrs = []
        cls_locals = classgen.get_class_locals(
            cls.name,
            allow_methods=True,
            ordering=classgen.Ordering.FIRST_ANNOTATE,
            vm=self.vm)
        for name, local in cls_locals.items():
            typ, orig = local.get_type(node, name), local.orig
            assert typ
            if match_classvar(typ):
                continue
            initvar_typ = self._handle_initvar(node, cls, name, typ, orig)
            if initvar_typ:
                typ = initvar_typ
                init = True
            else:
                if not orig:
                    cls.members[name] = classgen.instantiate(node, name, typ)
                if is_field(orig):
                    field = orig.data[0]
                    orig = field.default
                    init = field.init
                else:
                    init = True

            if (not self.vm.options.check_variable_types
                    or orig and orig.data == [self.vm.convert.none]):
                # vm._apply_annotation mostly takes care of checking that the default
                # matches the declared type. However, it allows None defaults, and
                # dataclasses do not.
                self.vm.check_annotation_type_mismatch(node,
                                                       name,
                                                       typ,
                                                       orig,
                                                       local.stack,
                                                       allow_none=False)

            attr = classgen.Attribute(name=name,
                                      typ=typ,
                                      init=init,
                                      default=orig)
            own_attrs.append(attr)

        base_attrs = self.get_base_class_attrs(cls, own_attrs,
                                               _DATACLASS_METADATA_KEY)
        attrs = base_attrs + own_attrs
        # Stash attributes in class metadata for subclasses.
        cls.metadata[_DATACLASS_METADATA_KEY] = attrs

        # Add an __init__ method if one doesn't exist already (dataclasses do not
        # overwrite an explicit __init__ method).
        if "__init__" not in cls.members and self.args[cls]["init"]:
            init_method = self.make_init(node, cls, attrs)
            cls.members["__init__"] = init_method
    def decorate(self, node, cls):
        """Processes class members."""

        # Collect classvars to convert them to attrs. @dataclass collects vars with
        # an explicit type annotation, in order of annotation, so that e.g.
        # class A:
        #   x: int
        #   y: str = 'hello'
        #   x = 10
        # would have init(x:int = 10, y:str = 'hello')
        own_attrs = []
        cls_locals = self.get_class_locals(node, cls)
        for name, local in cls_locals.items():
            typ, orig = local.get_type(node, name), local.orig
            kind = ""
            assert typ
            if match_classvar(typ):
                continue
            initvar_typ = self._handle_initvar(node, cls, name, typ, orig)
            if initvar_typ:
                typ = initvar_typ
                init = True
                kind = classgen.AttributeKinds.INITVAR
            else:
                if not orig:
                    cls.members[name] = classgen.instantiate(node, name, typ)
                if is_field(orig):
                    field = orig.data[0]
                    orig = field.default
                    init = field.init
                else:
                    init = True

            if orig and orig.data == [self.vm.convert.none]:
                # vm._apply_annotation mostly takes care of checking that the default
                # matches the declared type. However, it allows None defaults, and
                # dataclasses do not.
                self.vm.check_annotation_type_mismatch(node,
                                                       name,
                                                       typ,
                                                       orig,
                                                       local.stack,
                                                       allow_none=False)

            attr = classgen.Attribute(name=name,
                                      typ=typ,
                                      init=init,
                                      kw_only=False,
                                      default=orig,
                                      kind=kind)
            own_attrs.append(attr)

        cls.record_attr_ordering(own_attrs)
        attrs = cls.compute_attr_metadata(own_attrs, "dataclasses.dataclass")

        # Add an __init__ method if one doesn't exist already (dataclasses do not
        # overwrite an explicit __init__ method).
        if "__init__" not in cls.members and self.args[cls]["init"]:
            init_method = self.make_init(node, cls, attrs)
            cls.members["__init__"] = init_method

        if isinstance(cls, abstract.InterpreterClass):
            cls.decorators.append("dataclasses.dataclass")