formencode.compound
Covered: 17 lines
Missed: 153 lines
Skipped 49 lines
Percent: 10 %
  1
"""
  2
Validators for applying validations in sequence.
  3
"""
  5
from api import *
  8
__all__ = ['Any', 'All', 'Pipe']
 14
def to_python(validator, value, state):
 15
    return validator.to_python(value, state)
 17
def from_python(validator, value, state):
 18
    return validator.from_python(value, state)
 20
class CompoundValidator(FancyValidator):
 22
    if_invalid = NoDefault
 24
    validators = []
 26
    __unpackargs__ = ('*', 'validatorArgs')
 28
    __mutableattributes__ = ('validators',)
 30
    def __classinit__(cls, new_attrs):
 31
        toAdd = []
 32
        for name, value in new_attrs.items():
 33
            if name in ('view',):
 34
                continue
 35
            if is_validator(value) and value is not Identity:
 36
                toAdd.append((name, value))
 38
                delattr(cls, name)
 39
        toAdd.sort()
 40
        cls.validators.extend([v for n, v in toAdd])
 42
    def __init__(self, *args, **kw):
 43
        Validator.__init__(self, *args, **kw)
 44
        self.validators = self.validators[:]
 45
        self.validators.extend(self.validatorArgs)
 47
    def _reprVars(names):
 48
        return [n for n in Validator._reprVars(names)
 49
                if n != 'validatorArgs']
 50
    _reprVars = staticmethod(_reprVars)
 52
    def attempt_convert(self, value, state, convertFunc):
 53
        raise NotImplementedError, "Subclasses must implement attempt_convert"
 55
    def _to_python(self, value, state=None):
 56
        return self.attempt_convert(value, state,
 57
                                    to_python)
 59
    def _from_python(self, value, state=None):
 60
        return self.attempt_convert(value, state,
 61
                                    from_python)
 63
    def subvalidators(self):
 64
        return self.validators
 66
class Any(CompoundValidator):
 68
    """
 69
    This class is like an 'or' operator for validators.  The first
 70
    validator/converter that validates the value will be used.  (You
 71
    can pass in lists of validators, which will be ANDed)
 72
    """
 74
    def attempt_convert(self, value, state, validate):
 75
        lastException = None
 76
        if validate is to_python:
 77
            validators = self.validators[::-1]
 78
        else:
 79
            validators = self.validators
 80
        for validator in validators:
 81
            try:
 82
                return validate(validator, value, state)
 83
            except Invalid, e:
 84
                lastException = e
 85
        if self.if_invalid is NoDefault:
 86
            raise lastException
 87
        else:
 88
            return self.if_invalid
 90
    def not_empty__get(self):
 91
        not_empty = True
 92
        for validator in self.validators:
 93
            not_empty = not_empty and getattr(validator, 'not_empty', False)
 94
        return not_empty
 95
    not_empty = property(not_empty__get)
 97
    def is_empty(self, value):
 99
        return False
101
class All(CompoundValidator):
103
    """
104
    This class is like an 'and' operator for validators.  All
105
    validators must work, and the results are passed in turn through
106
    all validators for conversion.
107
    """
109
    def __repr__(self):
110
        return '<All %s>' % self.validators
112
    def attempt_convert(self, value, state, validate):
115
        if validate is to_python:
116
            validators = list(self.validators)
117
            validators.reverse()
118
        else:
119
            validators = self.validators
120
        try:
121
            for validator in validators:
122
                value = validate(validator, value, state)
123
            return value
124
        except Invalid:
125
            if self.if_invalid is NoDefault:
126
                raise
127
            return self.if_invalid
129
    def with_validator(self, validator):
130
        """
131
        Adds the validator (or list of validators) to a copy of
132
        this validator.
133
        """
134
        new = self.validators[:]
135
        if isinstance(validator, list) or isinstance(validator, tuple):
136
            new.extend(validator)
137
        else:
138
            new.append(validator)
139
        return self.__class__(*new, **{'if_invalid': self.if_invalid})
141
    def join(cls, *validators):
142
        """
143
        Joins several validators together as a single validator,
144
        filtering out None and trying to keep `All` validators from
145
        being nested (which isn't needed).
146
        """
147
        validators = filter(lambda v: v and v is not Identity, validators)
148
        if not validators:
149
            return Identity
150
        if len(validators) == 1:
151
            return validators[0]
152
        elif isinstance(validators[0], All):
153
            return validators[0].with_validator(validators[1:])
154
        else:
155
            return cls(*validators)
156
    join = classmethod(join)
158
    def if_missing__get(self):
159
        for validator in self.validators:
160
            v = validator.if_missing
161
            if v is not NoDefault:
162
                return v
163
        return NoDefault
164
    if_missing = property(if_missing__get)
166
    def not_empty__get(self):
167
        not_empty = False
168
        for validator in self.validators:
169
            not_empty = not_empty or getattr(validator, 'not_empty', False)
170
        return not_empty
171
    not_empty = property(not_empty__get)
173
    def is_empty(self, value):
175
        return False
177
class Pipe(All):
178
    """
179
    This class works like 'All', all validators muss pass, but the result
180
    of one validation pass is handled over to the next validator. A behaviour
181
    known to Unix and GNU users as 'pipe'.
183
    ::
185
        >>> from validators import DictConverter
186
        >>> pv = Pipe(validators=[DictConverter({1: 2}), DictConverter({2: 3}), DictConverter({3: 4})])
187
        >>> pv.to_python(1)
188
        4
189
        >>> pv.to_python(1)
190
        4
191
        >>> pv.from_python(4)
192
        1
193
        >>> pv.from_python(4)
194
        1
195
        >>> pv.to_python(1)
196
        4
198
    """
200
    def __repr__(self):
201
        return '<Pipe %s>' % self.validators
203
    def attempt_convert(self, value, state, validate):
206
        if validate is from_python:
207
            validators = list(self.validators)
208
            validators.reverse()
209
        else:
210
            validators = self.validators
211
        try:
212
            for validator in validators:
213
                value = validate(validator, value, state)
214
            return value
215
        except Invalid:
216
            if self.if_invalid is NoDefault:
217
                raise
218
            return self.if_invalid