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