mirror of
https://github.com/djohnlewis/stackdump
synced 2025-01-22 22:51:36 +00:00
222 lines
6.7 KiB
Python
222 lines
6.7 KiB
Python
"""
|
|
Validators for applying validations in sequence.
|
|
"""
|
|
|
|
from api import *
|
|
|
|
# @@ ianb 2005-05: should CompoundValidator be included?
|
|
__all__ = ['Any', 'All', 'Pipe']
|
|
|
|
############################################################
|
|
## Compound Validators
|
|
############################################################
|
|
|
|
def to_python(validator, value, state):
|
|
return validator.to_python(value, state)
|
|
|
|
|
|
def from_python(validator, value, state):
|
|
return validator.from_python(value, state)
|
|
|
|
|
|
class CompoundValidator(FancyValidator):
|
|
|
|
if_invalid = NoDefault
|
|
|
|
validators = []
|
|
|
|
__unpackargs__ = ('*', 'validatorArgs')
|
|
|
|
__mutableattributes__ = ('validators',)
|
|
|
|
def __classinit__(cls, new_attrs):
|
|
toAdd = []
|
|
for name, value in new_attrs.items():
|
|
if name in ('view',):
|
|
continue
|
|
if is_validator(value) and value is not Identity:
|
|
toAdd.append((name, value))
|
|
# @@: Should we really delete too?
|
|
delattr(cls, name)
|
|
toAdd.sort()
|
|
cls.validators.extend([v for n, v in toAdd])
|
|
|
|
def __init__(self, *args, **kw):
|
|
Validator.__init__(self, *args, **kw)
|
|
self.validators = self.validators[:]
|
|
self.validators.extend(self.validatorArgs)
|
|
|
|
def _reprVars(names):
|
|
return [n for n in Validator._reprVars(names)
|
|
if n != 'validatorArgs']
|
|
_reprVars = staticmethod(_reprVars)
|
|
|
|
def attempt_convert(self, value, state, convertFunc):
|
|
raise NotImplementedError, "Subclasses must implement attempt_convert"
|
|
|
|
def _to_python(self, value, state=None):
|
|
return self.attempt_convert(value, state,
|
|
to_python)
|
|
|
|
def _from_python(self, value, state=None):
|
|
return self.attempt_convert(value, state,
|
|
from_python)
|
|
|
|
def subvalidators(self):
|
|
return self.validators
|
|
|
|
|
|
class Any(CompoundValidator):
|
|
"""
|
|
This class is like an 'or' operator for validators. The first
|
|
validator/converter that validates the value will be used. (You
|
|
can pass in lists of validators, which will be ANDed)
|
|
"""
|
|
|
|
def attempt_convert(self, value, state, validate):
|
|
lastException = None
|
|
if validate is to_python:
|
|
validators = self.validators[::-1]
|
|
else:
|
|
validators = self.validators
|
|
for validator in validators:
|
|
try:
|
|
return validate(validator, value, state)
|
|
except Invalid, e:
|
|
lastException = e
|
|
if self.if_invalid is NoDefault:
|
|
raise lastException
|
|
else:
|
|
return self.if_invalid
|
|
|
|
def not_empty__get(self):
|
|
not_empty = True
|
|
for validator in self.validators:
|
|
not_empty = not_empty and getattr(validator, 'not_empty', False)
|
|
return not_empty
|
|
not_empty = property(not_empty__get)
|
|
|
|
def is_empty(self, value):
|
|
# sub-validators should handle emptiness.
|
|
return False
|
|
|
|
|
|
class All(CompoundValidator):
|
|
"""
|
|
This class is like an 'and' operator for validators. All
|
|
validators must work, and the results are passed in turn through
|
|
all validators for conversion.
|
|
"""
|
|
|
|
def __repr__(self):
|
|
return '<All %s>' % self.validators
|
|
|
|
def attempt_convert(self, value, state, validate):
|
|
# To preserve the order of the transformations, we do them
|
|
# differently when we are converting to and from python.
|
|
if validate is to_python:
|
|
validators = list(self.validators)
|
|
validators.reverse()
|
|
else:
|
|
validators = self.validators
|
|
try:
|
|
for validator in validators:
|
|
value = validate(validator, value, state)
|
|
return value
|
|
except Invalid:
|
|
if self.if_invalid is NoDefault:
|
|
raise
|
|
return self.if_invalid
|
|
|
|
def with_validator(self, validator):
|
|
"""
|
|
Adds the validator (or list of validators) to a copy of
|
|
this validator.
|
|
"""
|
|
new = self.validators[:]
|
|
if isinstance(validator, list) or isinstance(validator, tuple):
|
|
new.extend(validator)
|
|
else:
|
|
new.append(validator)
|
|
return self.__class__(*new, **dict(if_invalid=self.if_invalid))
|
|
|
|
def join(cls, *validators):
|
|
"""
|
|
Joins several validators together as a single validator,
|
|
filtering out None and trying to keep `All` validators from
|
|
being nested (which isn't needed).
|
|
"""
|
|
validators = filter(lambda v: v and v is not Identity, validators)
|
|
if not validators:
|
|
return Identity
|
|
if len(validators) == 1:
|
|
return validators[0]
|
|
elif isinstance(validators[0], All):
|
|
return validators[0].with_validator(validators[1:])
|
|
else:
|
|
return cls(*validators)
|
|
join = classmethod(join)
|
|
|
|
def if_missing__get(self):
|
|
for validator in self.validators:
|
|
v = validator.if_missing
|
|
if v is not NoDefault:
|
|
return v
|
|
return NoDefault
|
|
if_missing = property(if_missing__get)
|
|
|
|
def not_empty__get(self):
|
|
not_empty = False
|
|
for validator in self.validators:
|
|
not_empty = not_empty or getattr(validator, 'not_empty', False)
|
|
return not_empty
|
|
not_empty = property(not_empty__get)
|
|
|
|
def is_empty(self, value):
|
|
# sub-validators should handle emptiness.
|
|
return False
|
|
|
|
|
|
class Pipe(All):
|
|
"""
|
|
This class works like 'All', all validators muss pass, but the result
|
|
of one validation pass is handled over to the next validator. A behaviour
|
|
known to Unix and GNU users as 'pipe'.
|
|
|
|
::
|
|
|
|
>>> from validators import DictConverter
|
|
>>> pv = Pipe(validators=[DictConverter({1: 2}), DictConverter({2: 3}), DictConverter({3: 4})])
|
|
>>> pv.to_python(1)
|
|
4
|
|
>>> pv.to_python(1)
|
|
4
|
|
>>> pv.from_python(4)
|
|
1
|
|
>>> pv.from_python(4)
|
|
1
|
|
>>> pv.to_python(1)
|
|
4
|
|
|
|
"""
|
|
|
|
def __repr__(self):
|
|
return '<Pipe %s>' % self.validators
|
|
|
|
def attempt_convert(self, value, state, validate):
|
|
# To preserve the order of the transformations, we do them
|
|
# differently when we are converting to and from Python.
|
|
if validate is from_python:
|
|
validators = list(self.validators)
|
|
validators.reverse()
|
|
else:
|
|
validators = self.validators
|
|
try:
|
|
for validator in validators:
|
|
value = validate(validator, value, state)
|
|
return value
|
|
except Invalid:
|
|
if self.if_invalid is NoDefault:
|
|
raise
|
|
return self.if_invalid
|