1 """
2 Prompting the users for, and validating, answers.
3
4 In *qanda*, answers from a user may be processed through a list of validators.
5 This follows the idiom of Ian Bicking & FormEncode where validation and
6 conversion are one and the same: raw values are passed into a converter and the
7 results are passed into the next. Should an exception be raised, conversion is
8 halted, an error message printed (based on the exception message) and the
9 question posed again.
10
11 Additional validators are easy to construct. The minimum interface they need is
12 to be callable with a value and to return a (possibly transformed) value,
13 optionally throwing an exception if the value is not valid. Thus, type
14 constructors can be used as validators::
15
16 prompt.string ("Give me a float", converters=[float])
17
18 More complex validators can be derived from a supplied base class. BaseValidator
19 supplies three methods for overriding and customising validator behaviour:
20 ``__call__``, ``convert`` and ``validate``. Custom validators should only need
21 subclass one of these methods and perhaps supply a c'tor.
22
23 """
24
25
26
27 __docformat__ = "restructuredtext en"
28
29
30
31
32 import re
33 import exceptions
34
35 import defs
36
37 __all__ = [
38
39 ]
40
41
42
43
44
45
47 """
48 A base class for custom validators.
49
50 Ideally, this should make validator subclasses simple to construct. Derived
51 valuidators will often only have to override one method (of ``__call__``,
52 ``convert`` and ``validate``) and perhaps supply a c'tor.
53 """
54
56 """
57 Converts and validates user input.
58
59 :Parameters:
60 value
61 value to be checked or transformed
62
63 :Returns:
64 the transformed or validated value
65
66 Should throw an error if any problems. Override in subclass if required.
67 """
68
69 value = self.convert(value)
70 self.validate (value)
71 return value
72
74 """
75 Is this value correct or of the correct form?
76
77 :Parameters:
78 value
79 value to be checked
80
81 Should throw an exception if validations fails. Override in subclass if
82 required.
83 """
84
85
86 pass
87
89 """
90 Transform this value to the desired form.
91
92 :Parameters:
93 value
94 value to be transformed
95
96 :Returns:
97 the transformed value
98
99 Can throw if conversion fails. Override in subclass if required.
100 """
101
102 return value
103
104
105 -class Clean (BaseValidator):
106 """
107 Normalize values by stripping flanking space and converting to lower case.
108
109 Note that this does not explicitly throw errors.
110 """
112 return value.strip().lower()
113
114
116 """
117 Map values to other values.
118
119 Note that this does not explicitly throw errors. If a value is un-mapped,
120 it is simply returned.
121 """
123 """
124 :Parameters:
125 d
126 a dictionary mapping input values to output values
127 """
128 self._syns = d
129
131 return self._syns.get (value, value)
132
133
134 -class Vocab (BaseValidator):
135 """
136 Ensure values fall within a fixed set.
137 """
139 """
140 :Parameters:
141 args
142 a sequence of permitted values
143 """
144 self._allowed_values = args
145
147 assert value in self._allowed_values, "I don't understand '%s'" % value
148
149
151 """
152 Only allow non-blank strings (i.e. those with a length more than 0).
153 """
155 assert 0 < len(value), "can't be a blank string"
156
157
158 -class Regex (BaseValidator):
159 """
160 Only allow values that match a certain regular expression.
161 """
162
164 self.re = re.compile (patt)
165
167 assert self.re.match (value)
168
169
170 -class Range (BaseValidator):
171 """
172 Only allow values between certain inclusive bounds.
173 """
174 - def __init__ (self, min=None, max=None):
175 self.min = min
176 self.max = max
177
179 if self.min is not None:
180 assert self.min <= value, "%s is lower than %s" % (value, self.min)
181 if self.max is not None:
182 assert value <= self.max, "%s is higher than %s" % (value, self.max)
183
184
185 -class ToInt (BaseValidator):
186 """
187 Convert a value to an integer.
188
189 While you could just use ``int``, this throws a much nicer error message.
190 """
192 try:
193 conv_val = int (value)
194 return conv_val
195 except:
196 raise exceptions.ValueError ("not an integer")
197
198
200 """
201 Convert a value to a float.
202
203 While you could just use ``float``, this throws a much nicer error message.
204 """
206 try:
207 conv_val = float (value)
208 return conv_val
209 except:
210 raise exceptions.ValueError ("not a float")
211
212
214 """
215 Only allow values of a certain sizes.
216
217 Length limitations are expressed as (inclusive) minimum and maximum sizes.
218 This is most useful for strings, but could be used for lists.
219 """
220 - def __init__ (self, min=None, max=None):
221 self.min = min
222 self.max = max
223
225 if self.min is not None:
226 assert self.min <= len (value), "%s is lower than %s" % (value, self.min)
227 if self.max is not None:
228 assert len (value) <= self.max, "%s is higher than %s" % (value, self.max)
229
230
231
232
233