1
0
mirror of https://github.com/djohnlewis/stackdump synced 2025-01-22 22:51:36 +00:00
stackdump/python/packages/formencode/compound.py

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