Ejemplo n.º 1
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')
        ordered_locals = {
            x.name: x
            for x in self.get_class_locals(cls, allow_methods=True)
        }
        ordered_annotations = self.get_class_local_annotations(cls)
        own_attrs = []
        late_annotation = False  # True if we find a bare late annotation
        for local in ordered_annotations:
            name, value, orig = ordered_locals[local.name]
            if self.add_member(node, cls, name, value, orig):
                late_annotation = True

            if is_field(orig):
                field = orig.data[0]
                orig = field.typ
                init = field.init
            else:
                init = True

            # Check that default matches the declared type
            if orig and not classgen.is_late_annotation(value):
                typ = self.vm.convert.merge_classes(value.data)
                bad = self.vm.matcher.bad_matches(orig, typ, node)
                if bad:
                    binding = bad[0][orig]
                    # TODO(b/143487719): re-enable checking on None.
                    if binding.data != self.vm.convert.none:
                        self.vm.errorlog.annotation_type_mismatch(
                            self.vm.frames, typ, binding, name)

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

        # See if we need to resolve any late annotations
        if late_annotation:
            self.vm.classes_with_late_annotations.append(cls)

        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 self.args["init"]:
            init_method = self.make_init(node, attrs)
            cls.members["__init__"] = init_method
Ejemplo n.º 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
Ejemplo n.º 3
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(
            cls, allow_methods=True, ordering=classgen.Ordering.FIRST_ANNOTATE)
        for name, (value, orig) in cls_locals.items():
            clsvar = match_classvar(value)
            if clsvar:
                continue
            initvar = self._handle_initvar(node, cls, name, value, orig)
            if initvar:
                value = initvar.instantiate(node)
                init = True
            else:
                cls.members[name] = value
                if is_field(orig):
                    field = orig.data[0]
                    orig = field.typ if field.default else None
                    init = field.init
                else:
                    init = True

            # Check that default matches the declared type
            self._check_default(node, name, value, orig)

            attr = classgen.Attribute(name=name,
                                      typ=value,
                                      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
Ejemplo n.º 4
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
            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:
                    classgen.add_member(node, cls, 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")
            # Fix up type parameters in methods added by the decorator.
            cls.update_method_type_params()
Ejemplo n.º 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 = 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:
                    classgen.add_member(node, cls, 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.ctx.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.ctx.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]
                and self.args[cls]["init"]):
            init_method = self.make_init(node, cls, attrs)
            cls.members["__init__"] = init_method

        # Add the __dataclass_fields__ attribute, the presence of which
        # dataclasses.is_dataclass uses to determine if an object is a dataclass (or
        # an instance of one).
        attr_types = self.ctx.convert.merge_values(
            {attr.typ
             for attr in attrs})
        dataclass_ast = self.ctx.loader.import_name("dataclasses")
        generic_field = abstract.ParameterizedClass(
            self.ctx.convert.name_to_value("dataclasses.Field",
                                           ast=dataclass_ast),
            {abstract_utils.T: attr_types}, self.ctx)
        dataclass_fields_params = {
            abstract_utils.K: self.ctx.convert.str_type,
            abstract_utils.V: generic_field
        }
        dataclass_fields_typ = abstract.ParameterizedClass(
            self.ctx.convert.dict_type, dataclass_fields_params, self.ctx)
        classgen.add_member(node, cls, "__dataclass_fields__",
                            dataclass_fields_typ)

        annotations_dict = classgen.get_or_create_annotations_dict(
            cls.members, self.ctx)
        annotations_dict.annotated_locals["__dataclass_fields__"] = (
            abstract_utils.Local(node, None, dataclass_fields_typ, None,
                                 self.ctx))

        if isinstance(cls, abstract.InterpreterClass):
            cls.decorators.append("dataclasses.dataclass")
            # Fix up type parameters in methods added by the decorator.
            cls.update_method_type_params()
Ejemplo n.º 6
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