0001"""
0002Validators for applying validations in sequence.
0003"""
0004
0005from api import *
0006
0007# @@ ianb 2005-05: should CompoundValidator be included?
0008__all__ = ['Any', 'All']
0009
0010############################################################
0011## Compound Validators
0012############################################################
0013
0014def to_python(validator, value, state):
0015    return validator.to_python(value, state)
0016
0017def from_python(validator, value, state):
0018    return validator.from_python(value, state)
0019
0020class CompoundValidator(FancyValidator):
0021
0022    if_invalid = NoDefault
0023
0024    validators = []
0025
0026    __unpackargs__ = ('*', 'validatorArgs')
0027
0028    __mutableattributes__ = ('validators',)
0029
0030    def __classinit__(cls, new_attrs):
0031        toAdd = []
0032        for name, value in new_attrs.items():
0033            if name in ('view',):
0034                continue
0035            if is_validator(value) and value is not Identity:
0036                toAdd.append((name, value))
0037                # @@: Should we really delete too?
0038                delattr(cls, name)
0039        toAdd.sort()
0040        cls.validators.extend([v for n, v in toAdd])
0041
0042    def __init__(self, *args, **kw):
0043        Validator.__init__(self, *args, **kw)
0044        self.validators = self.validators[:]
0045        self.validators.extend(self.validatorArgs)
0046
0047    def _reprVars(names):
0048        return [n for n in Validator._reprVars(names)
0049                if n != 'validatorArgs']
0050    _reprVars = staticmethod(_reprVars)
0051
0052    def attempt_convert(self, value, state, convertFunc):
0053        raise NotImplementedError, "Subclasses must implement attempt_convert"
0054
0055    def _to_python(self, value, state=None):
0056        return self.attempt_convert(value, state,
0057                                    to_python)
0058
0059    def _from_python(self, value, state=None):
0060        return self.attempt_convert(value, state,
0061                                    from_python)
0062
0063    def subvalidators(self):
0064        return self.validators
0065
0066class Any(CompoundValidator):
0067
0068    """
0069    This class is like an 'or' operator for validators.  The first
0070    validator/converter that validates the value will be used.  (You
0071    can pass in lists of validators, which will be ANDed)
0072    """
0073
0074    def attempt_convert(self, value, state, validate):
0075        lastException = None
0076        if validate is to_python:
0077            validators = self.validators[::-1]
0078        else:
0079            validators = self.validators
0080        for validator in validators:
0081            try:
0082                return validate(validator, value, state)
0083            except Invalid, e:
0084                lastException = e
0085        if self.if_invalid is NoDefault:
0086            raise lastException
0087        else:
0088            return self.if_invalid
0089
0090    def not_empty__get(self):
0091        not_empty = True
0092        for validator in self.validators:
0093            not_empty = not_empty and getattr(validator, 'not_empty', False)
0094        return not_empty
0095    not_empty = property(not_empty__get)
0096
0097class All(CompoundValidator):
0098
0099    """
0100    This class is like an 'and' operator for validators.  All
0101    validators must work, and the results are passed in turn through
0102    all validators for conversion.
0103    """
0104
0105    def __repr__(self):
0106        return '<All %s>' % self.validators
0107
0108    def attempt_convert(self, value, state, validate):
0109        # To preserve the order of the transformations, we do them
0110        # differently when we are converting to and from python.
0111        if validate is to_python:
0112            validators = list(self.validators)
0113            validators.reverse()
0114        else:
0115            validators = self.validators
0116        try:
0117            for validator in validators:
0118                value = validate(validator, value, state)
0119            return value
0120        except Invalid:
0121            if self.if_invalid is NoDefault:
0122                raise
0123            return self.if_invalid
0124
0125    def with_validator(self, validator):
0126        """
0127        Adds the validator (or list of validators) to a copy of
0128        this validator.
0129        """
0130        new = self.validators[:]
0131        if isinstance(validator, list) or isinstance(validator, tuple):
0132            new.extend(validator)
0133        else:
0134            new.append(validator)
0135        return self.__class__(*new, **{'if_invalid': self.if_invalid})
0136
0137    def join(cls, *validators):
0138        """
0139        Joins several validators together as a single validator,
0140        filtering out None and trying to keep `All` validators from
0141        being nested (which isn't needed).
0142        """
0143        validators = filter(lambda v: v and v is not Identity, validators)
0144        if not validators:
0145            return Identity
0146        if len(validators) == 1:
0147            return validators[0]
0148        elif isinstance(validators[0], All):
0149            return validators[0].with_validator(validators[1:])
0150        else:
0151            return cls(*validators)
0152    join = classmethod(join)
0153
0154    def if_missing__get(self):
0155        for validator in self.validators:
0156            v = validator.if_missing
0157            if v is not NoDefault:
0158                return v
0159        return NoDefault
0160    if_missing = property(if_missing__get)
0161
0162    def not_empty__get(self):
0163        not_empty = False
0164        for validator in self.validators:
0165            not_empty = not_empty or getattr(validator, 'not_empty', False)
0166        return not_empty
0167    not_empty = property(not_empty__get)