def __init__(self, data): self.data = data self.name = data["name"] # We want to throw an exception if there is no name # use of jsontableschema.types to help constraint validation self.type = SchemaModel._type_map()[data.get("type")](data)
class SchemaField: """ Utility class for a field in a schema. It uses the schema types of https://github.com/frictionlessdata/jsontableschema-py#types for validation. """ # For most of the type we use the jsontableschema ones # TODO: SchemaModel is deprecated in favor of of # jsontableschema.schema.Schema but there's no _type_map! BASE_TYPE_MAP = SchemaModel._type_map() # except for anything date. BASE_TYPE_MAP['date'] = DayFirstDateType BASE_TYPE_MAP['datetime'] = DayFirstDateTimeType # and string BASE_TYPE_MAP['string'] = NotBlankStringType WL_TYPE_MAP = {} def __init__(self, data): self.data = data self.name = self.data.get('name') # We want to throw an exception if there is no name if not self.name: raise FieldSchemaError("A field without a name: {}".format( json.dumps(data))) # wl specific self.wl = WLSchema(self.data.get('wl')) # set the type: wl type as precedence type_class = self.WL_TYPE_MAP.get( self.wl.type) or self.BASE_TYPE_MAP.get(self.data.get('type')) self.type = type_class(self.data) self.constraints = SchemaConstraints(self.data.get('constraints', {})) # implement some dict like methods def __getitem__(self, item): return self.data.__getitem__(item) def get(self, k, d=None): return self.data.get(k, d) @property def title(self): return self.data.get('title') @property def column_name(self): return self.name @property def required(self): return self.constraints.required @property def is_species(self): return self.wl.is_species_type() @property def species_type(self): result = None if self.is_species: return self.wl.species_type or 'all' return result def cast(self, value): """ Returns a native Python object of the expected format. Will throw an exception if the value doesn't complies with any constraints. See for details: https://github.com/frictionlessdata/jsontableschema-py#types This method is mainly a helper for the validation_error :param value: :return: """ if isinstance(value, six.string_types) and not isinstance( value, six.text_type): # the StringType accepts only unicode value = six.u(value) elif isinstance(value, six.integer_types): value = '{}'.format(value) return self.type.cast(value) def validate(self, value): return self.validation_error(value) def validation_error(self, value): """ Return an error message if the value is not valid according to the schema. It relies on exception thrown by the 'cast1 method of Type method. :param value: :return: None if value is valid or an error message string """ error = None # override the integer validation. The default message is a bit cryptic if there's an error casting a string # like '1.2' into an int. if isinstance(self.type, types.IntegerType): if not is_blank_value(value): not_integer = False try: casted = self.cast(value) # there's also the case where the case where a float 1.2 is successfully casted in 1 # (ex: int(1.2) = 1) if str(casted) != str(value): not_integer = True except Exception: not_integer = True if not_integer: return 'The field "{}" must be a whole number.'.format( self.name) try: self.cast(value) except Exception as e: error = "{}".format(e) # Override the default enum exception message to include all # possible values if error.find('enum array') and self.constraints.enum: values = [str(v) for v in self.constraints.enum] error = "The value must be one the following: {}".format( values) return error def __str__(self): return '{}'.format(self.name)
def __init__(self, data): self.data = data self.name = data[ 'name'] # We want to throw an exception if there is no name # use of jsontableschema.types to help constraint validation self.type = SchemaModel._type_map()[data.get('type')](data)