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
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
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
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")