class FieldList(Field): __errors__ = { 'max_error': _("%(max)i items maximum permitted"), 'min_error': _("%(min)i items minimum permitted") } def __init__(self, *args, **kwargs): Field.__init__(self, *args, **kwargs) self.max = kwargs.get('max', None) self.min = kwargs.get('min', None) def validate(self, value, dependencies={}): errors = [] result = [] self._check_length(value) for item in value if isinstance(value, (list, tuple)) else [value]: try: result.append( Field.validate(self, item, dependencies=dependencies)) errors.append(None) except ValidationError as ve: errors.append(ve.error) if any(errors): raise ValidationError(errors) return result def _check_length(self, values): if self.max is not None and len(values) > self.max: self._validation_error('max_error', max=self.max) if self.min is not None and len(values) < self.min: self._validation_error('min_error', min=self.min)
class Int(Validator): __errors__ = {'not_int': _('This is not an integer')} def validate(self, value): if isinstance(value, int): return value self._validation_error('not_int')
class Email(Validator): __errors__ = {'no_valid': _('This is not a valid email')} # RFC 2822 REGEXP = '[a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])' def validate(self, value): if not re.match(self.REGEXP, value): self._validation_error('no_valid') return value
class In(Validator): __errors__ = {'not_in': _('Value must be in list [%(values)s]')} def __init__(self, *args, **kwargs): Validator.__init__(self, *args, **kwargs) self._values = args def validate(self, value): if value not in self._values: self._validation_error('not_in', values=', '.join(self._values)) return value
class String(Validator): __errors__ = { 'not_string': _("This is not a String"), 'empty': _('Please enter a value'), 'exact_length': _("Value length must be %(length)i exactly"), 'max_exceeded': _("Value length must be %(max)i or less"), 'min_not_reached': _("Value length must be %(min)i or more") } def __init__(self, *args, **kwargs): Validator.__init__(self, *args, **kwargs) self.max = kwargs.get('max', None) self.min = kwargs.get('min', None) self.empty = kwargs.get('empty', True) if self.max is not None and self.min is not None and self.min > self.max: raise ValueError("Max value must be greater than min") def validate(self, value): if not isinstance(value, basestring): self._validation_error('not_string') self.attr_validate(value) return value def attr_validate(self, value): if not self.empty and value == '': self._validation_error('empty') if self.max is not None: if self.min == self.max and self.max != len(value): self._validation_error('exact_length', length=self.max) if len(value) > self.max: self._validation_error('max_exceeded', max=self.max) if self.min is not None: if len(value) < self.min: self._validation_error('min_not_reached', min=self.min)
class Number(Validator): __errors__ = {'not_number': _('This is not a number')} def validate(self, value): try: f_result = float(value) try: i_result = int(value) return i_result if i_result == f_result else f_result except ValueError: pass return f_result except ValueError: self._validation_error('not_number')
def test_empty(self): self.VALIDATOR = String(empty=False) self.data = '' self.data_error(_('Please enter a value'))
def test_field_forbidden_present(self): self.data = {'field1': 'foo', 'field2': 'A', 'field5': 'foo'} self.data_error({'field5': _('Forbidden by conditions')})
def test_missing_multiple_value(self): self.data = {'field3': 'foo'} self.data_error({ 'field1': _('Missing value'), 'field2': _('Missing value') })
def test_missing_field_mandatory_optional(self): self.data = {'field1': 'A', 'field2': 'foo'} self.data_error({'field4': _('Missing value')})
def test_invalid_number(self): self.data = 'foo' self.data_error(_("This is not a number"))
def test_invalid_float(self): self.data = 2.03 self.data_error(_('This is not an integer'))
def test_mandatory_depends_root(self): self.data = {'guide': 'mandatory_condition', 'node': {'field': 'foo'}} self.data_error({'node': {'field_guide': _('Missing value')}})
def test_wrong_max(self): self.VALIDATOR = String(max=2) self.data = 'foo' self.data_error(_('Value length must be %(max)i or less') % {'max': 2})
def test_exact(self): self.VALIDATOR = String(max=4, min=4) self.data = 'foo' self.data_error(_('Value length must be %(length)i exactly') % {'length': 4})
def test_wrong_interval(self): try: String(max=4, min=6).validate('foo') except ValueError as e: assert_equals(_('Max value must be greater than min'), e.args[0])
def test_not_string(self): self.VALIDATOR = String() self.data = 9 self.data_error(_('This is not a String'))
def test_none(self): self.VALIDATOR = String() self.data = None self.data_error(_('This is not a String'))
def test_wrong_min(self): self.VALIDATOR = String(min=4) self.data = 'foo' self.data_error(_('Value length must be %(min)i or more') % {'min': 4})
def test_not_domain(self): self.data = 'foo@' self.data_error(_('This is not a valid email'))
def test_max_error(self): self.data = ['foo', 'bar', 'baz', 'foo2'] self.data_error(_('%(max)i items maximum permitted') % {'max': 3})
def test_invalid_string(self): self.data = '2' self.data_error(_('This is not an integer'))
def test_min_error(self): self.data = ['foo'] self.data_error(_('%(min)i items minimum permitted') % {'min': 2})
def test_missing_multiple_value(self): self.data = {'field3': 'foo'} self.data_error({'field1': _('Missing value'), 'field2': _('Missing value')})
def test_invalid(self): self.data = 'baz' self.data_error( _('Value must be in list [%(values)s]') % {'values': ', '.join(self.VALUES)})
def test_root_invalid(self): self.data = {'guide': 'foo'} self.data_error({'node': _('Missing value')})
class Schema(Validator): __metaclass__ = MetaSchema __errors__ = { 'missing': _('Missing value'), 'forbidden': _("Forbidden by conditions") } def validate(self, value): errors = {} result = {} self.__constant_fields_validation(value, result) self.__regular_fields_validation(value, result, errors) self.__combined_fields_validation(value, result, errors) if errors: raise ValidationError(errors) return result def __constant_fields_validation(self, value, result): for field, validator in self._constant_fields.iteritems(): result[field] = validator.value def __regular_fields_validation(self, value, result, errors): for field, validator in self._fields.iteritems(): try: if field in value or hasattr(validator, 'default'): self.__is_forbidden(validator, value) test_value = value[ field] if field in value else validator.default deps = self.__lookup_dependencies( validator.__dependencies__, value, result) result[field] = validator.validate(test_value, dependencies=deps) elif self.__is_mandatory(validator, value): self._validation_error('missing') except ValidationError as ve: errors[field] = ve.error def __combined_fields_validation(self, value, result, errors): for field, validator in self._combined_fields.iteritems(): try: if set(validator.__dependencies__).isdisjoint(errors.keys()): deps = self.__lookup_dependencies( validator.__dependencies__, value, result) validator.validate(value, dependencies=deps) except ValidationError as ve: errors[field if validator.destination is None else validator. destination] = ve.error def __is_forbidden(self, field, values): if hasattr(field, 'forbidden') and callable( field.forbidden) and field.forbidden(self, values): self._validation_error('forbidden') def __is_mandatory(self, field, values): if callable(field.mandatory): return field.mandatory(self, values) else: return field.mandatory def __lookup_dependencies(self, dependencies, values, result): return dict([(dep, values[dep] if dep in values else result[dep]) for dep in dependencies \ if dep in values or dep in result])
def test_no_user(self): self.data = '@foo.com' self.data_error(_('This is not a valid email'))
def test_exact(self): self.VALIDATOR = String(max=4, min=4) self.data = 'foo' self.data_error( _('Value length must be %(length)i exactly') % {'length': 4})
def test_no_dot(self): self.data = 'foo@host' self.data_error(_('This is not a valid email'))
def test_node_invalid(self): self.data = {'guide': 'foo', 'node': {}} self.data_error({'node': {'field': _('Missing value')}})
def test_invalid(self): self.data = 'baz' self.data_error(_('Value must be in list [%(values)s]') % {'values': ', '.join(self.VALUES)})