def __init__(self, fields: Sequence[Tuple[str, BaseSedes[Any, Any]]]) -> None: field_names = tuple(field_name for field_name, field_sedes in fields) duplicate_field_names = get_duplicates(field_names) if duplicate_field_names: raise ValueError( f"The following fields are duplicated {','.join(sorted(duplicate_field_names))}" ) self.fields = fields
def __init__(self, fields: Sequence[Tuple[str, BaseSedes[Any, Any]]]) -> None: self.fields = fields self.field_names = tuple(field_name for field_name, _ in self.fields) self.field_sedes_objects = tuple(field_sedes for _, field_sedes in self.fields) self.field_name_to_sedes = dict(self.fields) if len(fields) == 0: raise ValidationError("Cannot define container without any fields") duplicate_field_names = get_duplicates(self.field_names) if duplicate_field_names: raise ValidationError( f"The following fields are duplicated {','.join(sorted(duplicate_field_names))}" )
def _validate_field_names(field_names: Sequence[str]) -> None: # check that field names are unique duplicate_field_names = get_duplicates(field_names) if duplicate_field_names: raise TypeError("The following fields are duplicated in the `fields` " "declaration: " "{0}".format(",".join(sorted(duplicate_field_names)))) # check that field names are valid identifiers invalid_field_names = { field_name for field_name in field_names if not _is_valid_identifier(field_name) } if invalid_field_names: raise TypeError( "The following field names are not valid python identifiers: {0}". format(",".join("`{0}`".format(item) for item in sorted(invalid_field_names))))
def validate_args_and_kwargs(args, kwargs, arg_names): duplicate_arg_names = get_duplicates(arg_names) if duplicate_arg_names: raise ValueError("Duplicate argument names: {0}".format(sorted(duplicate_arg_names))) needed_arg_names = set(arg_names[len(args):]) used_arg_names = set(arg_names[:len(args)]) duplicate_arg_names = used_arg_names.intersection(kwargs.keys()) if duplicate_arg_names: raise TypeError("Duplicate kwargs: {0}".format(sorted(duplicate_arg_names))) unknown_arg_names = set(kwargs.keys()).difference(arg_names) if unknown_arg_names: raise TypeError("Unknown kwargs: {0}".format(sorted(unknown_arg_names))) missing_arg_names = set(needed_arg_names).difference(kwargs.keys()) if missing_arg_names: raise TypeError("Missing kwargs: {0}".format(sorted(missing_arg_names)))
def __new__(cls, name, bases, attrs): super_new = super(SerializableBase, cls).__new__ serializable_bases = tuple(b for b in bases if isinstance(b, SerializableBase)) has_multiple_serializable_parents = len(serializable_bases) > 1 is_serializable_subclass = any(serializable_bases) declares_fields = 'fields' in attrs if not is_serializable_subclass: # If this is the original creation of the `Serializable` class, # just create the class. return super_new(cls, name, bases, attrs) elif not declares_fields: if has_multiple_serializable_parents: raise TypeError( "Cannot create subclass from multiple parent `Serializable` " "classes without explicit `fields` declaration.") else: # This is just a vanilla subclass of a `Serializable` parent class. parent_serializable = serializable_bases[0] if hasattr(parent_serializable, '_meta'): fields = parent_serializable._meta.fields else: # This is a subclass of `Serializable` which has no # `fields`, likely intended for further subclassing. fields = () else: # ensure that the `fields` property is a tuple of tuples to ensure # immutability. fields = tuple(tuple(field) for field in attrs.pop('fields')) # split the fields into names and sedes if fields: field_names, sedes = zip(*fields) else: field_names = () # check that field names are unique duplicate_field_names = get_duplicates(field_names) if duplicate_field_names: raise TypeError( "The following fields are duplicated in the `fields` " "declaration: " "{0}".format(",".join(sorted(duplicate_field_names)))) # check that field names are valid identifiers invalid_field_names = { field_name for field_name in field_names if not _is_valid_identifier(field_name) } if invalid_field_names: raise TypeError( "The following field names are not valid python identifiers: {0}" .format(",".join("`{0}`".format(item) for item in sorted(invalid_field_names)))) # extract all of the fields from parent `Serializable` classes. parent_field_names = { field_name for base in serializable_bases if hasattr(base, '_meta') for field_name in base._meta.field_names } # check that all fields from parent serializable classes are # represented on this class. missing_fields = parent_field_names.difference(field_names) if missing_fields: raise TypeError( "Subclasses of `Serializable` **must** contain a full superset " "of the fields defined in their parent classes. The following " "fields are missing: " "{0}".format(",".join(sorted(missing_fields)))) # the actual field values are stored in separate *private* attributes. # This computes attribute names that don't conflict with other # attributes already present on the class. reserved_namespace = set(attrs.keys()).union( attr for base in bases for parent_cls in base.__mro__ for attr in _get_class_namespace(parent_cls)) field_attrs = _mk_field_attrs(field_names, reserved_namespace) # construct the Meta object to store field information for the class meta_namespace = { 'fields': fields, 'field_attrs': field_attrs, 'field_names': field_names, 'container_sedes': Container(fields), } meta_base = attrs.pop('_meta', MetaBase) meta = type( 'Meta', (meta_base, ), meta_namespace, ) attrs['_meta'] = meta # construct `property` attributes for read only access to the fields. field_props = tuple( (field, _mk_field_property(field, attr)) for field, attr in zip(meta.field_names, meta.field_attrs)) return super_new( cls, name, bases, dict(field_props + tuple(attrs.items())), )