Package qanda :: Module validators
[hide private]
[frames] | no frames]

Source Code for Module qanda.validators

  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  # TODO: "or" validator 
 25   
 26   
 27  __docformat__ = "restructuredtext en" 
 28   
 29   
 30  ### IMPORTS 
 31   
 32  import re 
 33  import exceptions 
 34   
 35  import defs 
 36   
 37  __all__ = [ 
 38   
 39  ] 
 40   
 41   
 42  ### CONSTANTS & DEFINES 
 43   
 44  ### IMPLEMENTATION ### 
 45   
46 -class BaseValidator (object):
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
55 - def __call__ (self, value):
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 # NOTE: override in subclass 69 value = self.convert(value) 70 self.validate (value) 71 return value
72
73 - def validate (self, value):
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 # NOTE: override in subclass 85 # probably a series of assertions 86 pass
87
88 - def convert (self, value):
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 # NOTE: override in subclass 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 """
111 - def convert (self, value):
112 return value.strip().lower()
113 114
115 -class Synonyms (BaseValidator):
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 """
122 - def __init__ (self, d):
123 """ 124 :Parameters: 125 d 126 a dictionary mapping input values to output values 127 """ 128 self._syns = d
129
130 - def convert (self, value):
131 return self._syns.get (value, value)
132 133
134 -class Vocab (BaseValidator):
135 """ 136 Ensure values fall within a fixed set. 137 """
138 - def __init__ (self, args):
139 """ 140 :Parameters: 141 args 142 a sequence of permitted values 143 """ 144 self._allowed_values = args
145
146 - def validate (self, value):
147 assert value in self._allowed_values, "I don't understand '%s'" % value
148 149
150 -class Nonblank (BaseValidator):
151 """ 152 Only allow non-blank strings (i.e. those with a length more than 0). 153 """
154 - def validate (self, value):
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 # TODO: compile flags?
163 - def __init__ (self, patt):
164 self.re = re.compile (patt)
165
166 - def validate (self, value):
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
178 - def validate (self, value):
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 """
191 - def convert (self, value):
192 try: 193 conv_val = int (value) 194 return conv_val 195 except: 196 raise exceptions.ValueError ("not an integer")
197 198
199 -class ToFloat (BaseValidator):
200 """ 201 Convert a value to a float. 202 203 While you could just use ``float``, this throws a much nicer error message. 204 """
205 - def convert (self, value):
206 try: 207 conv_val = float (value) 208 return conv_val 209 except: 210 raise exceptions.ValueError ("not a float")
211 212
213 -class Length (BaseValidator):
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
224 - def validate (self, value):
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 ### END ####################################################################### 233