-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcompound.py
More file actions
218 lines (181 loc) · 6.68 KB
/
compound.py
File metadata and controls
218 lines (181 loc) · 6.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
"""
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, **{'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