Esempio n. 1
0
 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)
Esempio n. 3
0
 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)