/[theodore]/bunnyblog/modules/validate.py


UCC Code Repository

Contents of /bunnyblog/modules/validate.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations) (download) (as text)
Tue Jan 29 14:32:01 2008 UTC (12 years, 2 months ago) by svn-admin
File MIME type: text/x-python
File size: 38775 byte(s)
Re-import of repository after repository database corruption.

1 svn-admin 1 # validate.py
2     # A Validator object
3     # Copyright (C) 2005 Michael Foord, Mark Andrews, Nicola Larosa
4     # E-mail: fuzzyman AT voidspace DOT org DOT uk
5     # mark AT la-la DOT com
6     # nico AT tekNico DOT net
7    
8     # This software is licensed under the terms of the BSD license.
9     # http://www.voidspace.org.uk/documents/BSD-LICENSE.txt
10     # Basically you're free to copy, modify, distribute and relicense it,
11     # So long as you keep a copy of the license with it.
12    
13     # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
14     # For information about bugfixes, updates and support, please join the
15     # ConfigObj mailing list:
16     # http://lists.sourceforge.net/lists/listinfo/configobj-develop
17     # Comments, suggestions and bug reports welcome.
18    
19     """
20     The Validator object is used to check that supplied values
21     conform to a specification.
22    
23     The value can be supplied as a string - e.g. from a config file.
24     In this case the check will also *convert* the value to
25     the required type. This allows you to add validation
26     as a transparent layer to access data stored as strings.
27     The validation checks that the data is correct *and*
28     converts it to the expected type.
29    
30     Some standard checks are provided for basic data types.
31     Additional checks are easy to write. They can be
32     provided when the ``Validator`` is instantiated or
33     added afterwards.
34    
35     The standard functions work with the following basic data types :
36    
37     * integers
38     * floats
39     * booleans
40     * strings
41     * ip_addr
42    
43     plus lists of these datatypes
44    
45     Adding additional checks is done through coding simple functions.
46    
47     The full set of standard checks are :
48    
49     * 'integer': matches integer values (including negative)
50     Takes optional 'min' and 'max' arguments : ::
51    
52     integer()
53     integer(3, 9) # any value from 3 to 9
54     integer(min=0) # any positive value
55     integer(max=9)
56    
57     * 'float': matches float values
58     Has the same parameters as the integer check.
59    
60     * 'boolean': matches boolean values - ``True`` or ``False``
61     Acceptable string values for True are :
62     true, on, yes, 1
63     Acceptable string values for False are :
64     false, off, no, 0
65    
66     Any other value raises an error.
67    
68     * 'ip_addr': matches an Internet Protocol address, v.4, represented
69     by a dotted-quad string, i.e. '1.2.3.4'.
70    
71     * 'string': matches any string.
72     Takes optional keyword args 'min' and 'max'
73     to specify min and max lengths of the string.
74    
75     * 'list': matches any list.
76     Takes optional keyword args 'min', and 'max' to specify min and
77     max sizes of the list.
78    
79     * 'int_list': Matches a list of integers.
80     Takes the same arguments as list.
81    
82     * 'float_list': Matches a list of floats.
83     Takes the same arguments as list.
84    
85     * 'bool_list': Matches a list of boolean values.
86     Takes the same arguments as list.
87    
88     * 'ip_addr_list': Matches a list of IP addresses.
89     Takes the same arguments as list.
90    
91     * 'string_list': Matches a list of strings.
92     Takes the same arguments as list.
93    
94     * 'mixed_list': Matches a list with different types in
95     specific positions. List size must match
96     the number of arguments.
97    
98     Each position can be one of :
99     'integer', 'float', 'ip_addr', 'string', 'boolean'
100    
101     So to specify a list with two strings followed
102     by two integers, you write the check as : ::
103    
104     mixed_list('string', 'string', 'integer', 'integer')
105    
106     * 'pass': This check matches everything ! It never fails
107     and the value is unchanged.
108    
109     It is also the default if no check is specified.
110    
111     * 'option': This check matches any from a list of options.
112     You specify this check with : ::
113    
114     option('option 1', 'option 2', 'option 3')
115     """
116    
117     __docformat__ = "restructuredtext en"
118    
119     __version__ = '0.2.0'
120    
121     __revision__ = '$Id: validate.py 123 2005-09-08 08:54:28Z fuzzyman $'
122    
123     __all__ = (
124     '__version__',
125     'dottedQuadToNum',
126     'numToDottedQuad',
127     'ValidateError',
128     'VdtUnknownCheckError',
129     'VdtParamError',
130     'VdtTypeError',
131     'VdtValueError',
132     'VdtValueTooSmallError',
133     'VdtValueTooBigError',
134     'VdtValueTooShortError',
135     'VdtValueTooLongError',
136     'VdtMissingValue',
137     'Validator',
138     'is_integer',
139     'is_float',
140     'is_bool',
141     'is_list',
142     'is_ip_addr',
143     'is_string',
144     'is_int_list',
145     'is_bool_list',
146     'is_float_list',
147     'is_string_list',
148     'is_ip_addr_list',
149     'is_mixed_list',
150     'is_option',
151     '__docformat__',
152     )
153    
154     import sys
155     INTP_VER = sys.version_info[:2]
156     if INTP_VER < (2, 2):
157     raise RuntimeError("Python v.2.2 or later needed")
158    
159     import re
160     StringTypes = (str, unicode)
161    
162     # Python pre 2.2.1 doesn't have bool
163     try:
164     bool
165     except NameError:
166     def bool(val):
167     """Simple boolean equivalent function. """
168     if val:
169     return 1
170     else:
171     return 0
172    
173     def dottedQuadToNum(ip):
174     """
175     Convert decimal dotted quad string to long integer
176    
177     >>> dottedQuadToNum('1 ')
178     1L
179     >>> dottedQuadToNum(' 1.2')
180     16777218L
181     >>> dottedQuadToNum(' 1.2.3 ')
182     16908291L
183     >>> dottedQuadToNum('1.2.3.4')
184     16909060L
185     >>> dottedQuadToNum('1.2.3. 4')
186     Traceback (most recent call last):
187     ValueError: Not a good dotted-quad IP: 1.2.3. 4
188     >>> dottedQuadToNum('255.255.255.255')
189     4294967295L
190     >>> dottedQuadToNum('255.255.255.256')
191     Traceback (most recent call last):
192     ValueError: Not a good dotted-quad IP: 255.255.255.256
193     """
194    
195     # import here to avoid it when ip_addr values are not used
196     import socket, struct
197    
198     try:
199     return struct.unpack('!L',
200     socket.inet_aton(ip.strip()))[0]
201     except socket.error:
202     # bug in inet_aton, corrected in Python 2.3
203     if ip.strip() == '255.255.255.255':
204     return 0xFFFFFFFFL
205     else:
206     raise ValueError('Not a good dotted-quad IP: %s' % ip)
207     return
208    
209     def numToDottedQuad(num):
210     """
211     Convert long int to dotted quad string
212    
213     >>> numToDottedQuad(-1L)
214     Traceback (most recent call last):
215     ValueError: Not a good numeric IP: -1
216     >>> numToDottedQuad(1L)
217     '0.0.0.1'
218     >>> numToDottedQuad(16777218L)
219     '1.0.0.2'
220     >>> numToDottedQuad(16908291L)
221     '1.2.0.3'
222     >>> numToDottedQuad(16909060L)
223     '1.2.3.4'
224     >>> numToDottedQuad(4294967295L)
225     '255.255.255.255'
226     >>> numToDottedQuad(4294967296L)
227     Traceback (most recent call last):
228     ValueError: Not a good numeric IP: 4294967296
229     """
230    
231     # import here to avoid it when ip_addr values are not used
232     import socket, struct
233    
234     # no need to intercept here, 4294967295L is fine
235     try:
236     return socket.inet_ntoa(
237     struct.pack('!L', long(num)))
238     except (socket.error, struct.error, OverflowError):
239     raise ValueError('Not a good numeric IP: %s' % num)
240    
241     class ValidateError(Exception):
242     """
243     This error indicates that the check failed.
244     It can be the base class for more specific errors.
245    
246     Any check function that fails ought to raise this error.
247     (or a subclass)
248    
249     >>> raise ValidateError
250     Traceback (most recent call last):
251     ValidateError
252     """
253    
254     class VdtMissingValue(ValidateError):
255     """No value was supplied to a check that needed one."""
256    
257     class VdtUnknownCheckError(ValidateError):
258     """An unknown check function was requested"""
259    
260     def __init__(self, value):
261     """
262     >>> raise VdtUnknownCheckError('yoda')
263     Traceback (most recent call last):
264     VdtUnknownCheckError: the check "yoda" is unknown.
265     """
266     ValidateError.__init__(
267     self,
268     'the check "%s" is unknown.' % value)
269    
270     class VdtParamError(SyntaxError):
271     """An incorrect parameter was passed"""
272    
273     def __init__(self, name, value):
274     """
275     >>> raise VdtParamError('yoda', 'jedi')
276     Traceback (most recent call last):
277     VdtParamError: passed an incorrect value "jedi" for parameter "yoda".
278     """
279     SyntaxError.__init__(
280     self,
281     'passed an incorrect value "%s" for parameter "%s".' % (
282     value, name))
283    
284     class VdtTypeError(ValidateError):
285     """The value supplied was of the wrong type"""
286    
287     def __init__(self, value):
288     """
289     >>> raise VdtTypeError('jedi')
290     Traceback (most recent call last):
291     VdtTypeError: the value "jedi" is of the wrong type.
292     """
293     ValidateError.__init__(
294     self,
295     'the value "%s" is of the wrong type.' % value)
296    
297     class VdtValueError(ValidateError):
298     """
299     The value supplied was of the correct type, but was not an allowed value.
300     """
301    
302     def __init__(self, value):
303     """
304     >>> raise VdtValueError('jedi')
305     Traceback (most recent call last):
306     VdtValueError: the value "jedi" is unacceptable.
307     """
308     ValidateError.__init__(
309     self,
310     'the value "%s" is unacceptable.' % value)
311    
312     class VdtValueTooSmallError(VdtValueError):
313     """The value supplied was of the correct type, but was too small."""
314    
315     def __init__(self, value):
316     """
317     >>> raise VdtValueTooSmallError('0')
318     Traceback (most recent call last):
319     VdtValueTooSmallError: the value "0" is too small.
320     """
321     ValidateError.__init__(
322     self,
323     'the value "%s" is too small.' % value)
324    
325     class VdtValueTooBigError(VdtValueError):
326     """The value supplied was of the correct type, but was too big."""
327    
328     def __init__(self, value):
329     """
330     >>> raise VdtValueTooBigError('1')
331     Traceback (most recent call last):
332     VdtValueTooBigError: the value "1" is too big.
333     """
334     ValidateError.__init__(
335     self,
336     'the value "%s" is too big.' % value)
337    
338     class VdtValueTooShortError(VdtValueError):
339     """The value supplied was of the correct type, but was too short."""
340    
341     def __init__(self, value):
342     """
343     >>> raise VdtValueTooShortError('jed')
344     Traceback (most recent call last):
345     VdtValueTooShortError: the value "jed" is too short.
346     """
347     ValidateError.__init__(
348     self,
349     'the value "%s" is too short.' % (value,))
350    
351     class VdtValueTooLongError(VdtValueError):
352     """The value supplied was of the correct type, but was too long."""
353    
354     def __init__(self, value):
355     """
356     >>> raise VdtValueTooLongError('jedie')
357     Traceback (most recent call last):
358     VdtValueTooLongError: the value "jedie" is too long.
359     """
360     ValidateError.__init__(
361     self,
362     'the value "%s" is too long.' % (value,))
363    
364     class Validator(object):
365     """
366     Validator is an object that allows you to register a set of 'checks'.
367     These checks take input and test that it conforms to the check.
368    
369     This can also involve converting the value from a string into
370     the correct datatype.
371    
372     The ``check`` method takes an input string which configures which
373     check is to be used and applies that check to a supplied value.
374    
375     An example input string would be:
376     'int_range(param1, param2)'
377    
378     You would then provide something like:
379    
380     >>> def int_range_check(value, min, max):
381     ... # turn min and max from strings to integers
382     ... min = int(min)
383     ... max = int(max)
384     ... # check that value is of the correct type.
385     ... # possible valid inputs are integers or strings
386     ... # that represent integers
387     ... if not isinstance(value, (int, long, StringTypes)):
388     ... raise VdtTypeError(value)
389     ... elif isinstance(value, StringTypes):
390     ... # if we are given a string
391     ... # attempt to convert to an integer
392     ... try:
393     ... value = int(value)
394     ... except ValueError:
395     ... raise VdtValueError(value)
396     ... # check the value is between our constraints
397     ... if not min <= value:
398     ... raise VdtValueTooSmallError(value)
399     ... if not value <= max:
400     ... raise VdtValueTooBigError(value)
401     ... return value
402    
403     >>> fdict = {'int_range': int_range_check}
404     >>> vtr1 = Validator(fdict)
405     >>> vtr1.check('int_range(20, 40)', '30')
406     30
407     >>> vtr1.check('int_range(20, 40)', '60')
408     Traceback (most recent call last):
409     VdtValueTooBigError: the value "60" is too big.
410    
411     New functions can be added with : ::
412    
413     >>> vtr2 = Validator()
414     >>> vtr2.functions['int_range'] = int_range_check
415    
416     Or by passing in a dictionary of functions when Validator
417     is instantiated.
418    
419     Your functions *can* use keyword arguments,
420     but the first argument should always be 'value'.
421    
422     If the function doesn't take additional arguments,
423     the parentheses are optional in the check.
424     It can be written with either of : ::
425    
426     keyword = function_name
427     keyword = function_name()
428    
429     The first program to utilise Validator() was Michael Foord's
430     ConfigObj, an alternative to ConfigParser which supports lists and
431     can validate a config file using a config schema.
432     For more details on using Validator with ConfigObj see:
433     http://www.voidspace.org.uk/python/configobj.html
434     """
435    
436     # this regex pulls values out of a comma separated line
437     _paramfinder = re.compile(r'''(?:'.*?')|(?:".*?")|(?:[^'",\s][^,]*)''')
438     # this regex is used for finding keyword arguments
439     _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$')
440     # this regex does the initial parsing of the checks
441     _func_re = re.compile(r'(.+?)\((.*)\)')
442    
443     def __init__(self, functions=None):
444     """
445     >>> vtri = Validator()
446     """
447     self.functions = {
448     '': self._pass,
449     'integer': is_integer,
450     'float': is_float,
451     'boolean': is_bool,
452     'ip_addr': is_ip_addr,
453     'string': is_string,
454     'list': is_list,
455     'int_list': is_int_list,
456     'float_list': is_float_list,
457     'bool_list': is_bool_list,
458     'ip_addr_list': is_ip_addr_list,
459     'string_list': is_string_list,
460     'mixed_list': is_mixed_list,
461     'pass': self._pass,
462     'option': is_option,
463     }
464     if functions is not None:
465     self.functions.update(functions)
466     # tekNico: for use by ConfigObj
467     self.baseErrorClass = ValidateError
468    
469     def check(self, check, value, missing=False):
470     """
471     Usage: check(check, value)
472    
473     Arguments:
474     check: string representing check to apply (including arguments)
475     value: object to be checked
476     Returns value, converted to correct type if necessary
477    
478     If the check fails, raises a ``ValidateError`` subclass.
479    
480     >>> vtor.check('yoda', '')
481     Traceback (most recent call last):
482     VdtUnknownCheckError: the check "yoda" is unknown.
483     >>> vtor.check('yoda()', '')
484     Traceback (most recent call last):
485     VdtUnknownCheckError: the check "yoda" is unknown.
486     """
487     fun_match = self._func_re.match(check)
488     if fun_match:
489     fun_name = fun_match.group(1)
490     fun_args = []
491     fun_kwargs = {}
492     # pull out args of group 2
493     for arg in self._paramfinder.findall(fun_match.group(2)):
494     # args may need whitespace removing (before removing quotes)
495     arg = arg.strip()
496     keymatch = self._key_arg.match(arg)
497     if keymatch:
498     val = keymatch.group(2)
499     if (val[0] in ("'", '"')) and (val[0] == val[-1]):
500     val = val[1:-1]
501     fun_kwargs[keymatch.group(1)] = val
502     continue
503     #
504     if (arg[0] in ("'", '"')) and (arg[0] == arg[-1]):
505     arg = arg[1:-1]
506     fun_args.append(arg)
507     else:
508     # allows for function names without (args)
509     (fun_name, fun_args, fun_kwargs) = (check, (), {})
510     #
511     if missing:
512     try:
513     value = fun_kwargs['default']
514     except KeyError:
515     raise VdtMissingValue
516     if value == 'None':
517     value = None
518     if value is None:
519     return None
520     # tekNico: default must be deleted if the value is specified too,
521     # otherwise the check function will get a spurious "default" keyword arg
522     try:
523     del fun_kwargs['default']
524     except KeyError:
525     pass
526     try:
527     return self.functions[fun_name](value, *fun_args, **fun_kwargs)
528     except KeyError:
529     raise VdtUnknownCheckError(fun_name)
530    
531     def _pass(self, value):
532     """
533     Dummy check that always passes
534    
535     >>> vtor.check('', 0)
536     0
537     >>> vtor.check('', '0')
538     '0'
539     """
540     return value
541    
542    
543     def _is_num_param(names, values, to_float=False):
544     """
545     Return numbers from inputs or raise VdtParamError.
546    
547     Lets ``None`` pass through.
548     Pass in keyword argument ``to_float=True`` to
549     use float for the conversion rather than int.
550    
551     >>> _is_num_param(('', ''), (0, 1.0))
552     [0, 1]
553     >>> _is_num_param(('', ''), (0, 1.0), to_float=True)
554     [0.0, 1.0]
555     >>> _is_num_param(('a'), ('a'))
556     Traceback (most recent call last):
557     VdtParamError: passed an incorrect value "a" for parameter "a".
558     """
559     fun = to_float and float or int
560     out_params = []
561     for (name, val) in zip(names, values):
562     if val is None:
563     out_params.append(val)
564     elif isinstance(val, (int, long, float, StringTypes)):
565     try:
566     out_params.append(fun(val))
567     except ValueError, e:
568     raise VdtParamError(name, val)
569     else:
570     raise VdtParamError(name, val)
571     return out_params
572    
573     # built in checks
574     # you can override these by setting the appropriate name
575     # in Validator.functions
576     # note: if the params are specified wrongly in your input string,
577     # you will also raise errors.
578    
579     def is_integer(value, min=None, max=None):
580     """
581     A check that tests that a given value is an integer (int, or long)
582     and optionally, between bounds. A negative value is accepted, while
583     a float will fail.
584    
585     If the value is a string, then the conversion is done - if possible.
586     Otherwise a VdtError is raised.
587    
588     >>> vtor.check('integer', '-1')
589     -1
590     >>> vtor.check('integer', '0')
591     0
592     >>> vtor.check('integer', 9)
593     9
594     >>> vtor.check('integer', 'a')
595     Traceback (most recent call last):
596     VdtTypeError: the value "a" is of the wrong type.
597     >>> vtor.check('integer', '2.2')
598     Traceback (most recent call last):
599     VdtTypeError: the value "2.2" is of the wrong type.
600     >>> vtor.check('integer(10)', '20')
601     20
602     >>> vtor.check('integer(max=20)', '15')
603     15
604     >>> vtor.check('integer(10)', '9')
605     Traceback (most recent call last):
606     VdtValueTooSmallError: the value "9" is too small.
607     >>> vtor.check('integer(10)', 9)
608     Traceback (most recent call last):
609     VdtValueTooSmallError: the value "9" is too small.
610     >>> vtor.check('integer(max=20)', '35')
611     Traceback (most recent call last):
612     VdtValueTooBigError: the value "35" is too big.
613     >>> vtor.check('integer(max=20)', 35)
614     Traceback (most recent call last):
615     VdtValueTooBigError: the value "35" is too big.
616     >>> vtor.check('integer(0, 9)', False)
617     0
618     """
619     # print value, type(value)
620     (min_val, max_val) = _is_num_param(('min', 'max'), (min, max))
621     if not isinstance(value, (int, long, StringTypes)):
622     raise VdtTypeError(value)
623     if isinstance(value, StringTypes):
624     # if it's a string - does it represent an integer ?
625     try:
626     value = int(value)
627     except ValueError:
628     raise VdtTypeError(value)
629     if (min_val is not None) and (value < min_val):
630     raise VdtValueTooSmallError(value)
631     if (max_val is not None) and (value > max_val):
632     raise VdtValueTooBigError(value)
633     return value
634    
635     def is_float(value, min=None, max=None):
636     """
637     A check that tests that a given value is a float
638     (an integer will be accepted), and optionally - that it is between bounds.
639    
640     If the value is a string, then the conversion is done - if possible.
641     Otherwise a VdtError is raised.
642    
643     This can accept negative values.
644    
645     >>> vtor.check('float', '2')
646     2.0
647    
648     From now on we multiply the value to avoid comparing decimals
649    
650     >>> vtor.check('float', '-6.8') * 10
651     -68.0
652     >>> vtor.check('float', '12.2') * 10
653     122.0
654     >>> vtor.check('float', 8.4) * 10
655     84.0
656     >>> vtor.check('float', 'a')
657     Traceback (most recent call last):
658     VdtTypeError: the value "a" is of the wrong type.
659     >>> vtor.check('float(10.1)', '10.2') * 10
660     102.0
661     >>> vtor.check('float(max=20.2)', '15.1') * 10
662     151.0
663     >>> vtor.check('float(10.0)', '9.0')
664     Traceback (most recent call last):
665     VdtValueTooSmallError: the value "9.0" is too small.
666     >>> vtor.check('float(max=20.0)', '35.0')
667     Traceback (most recent call last):
668     VdtValueTooBigError: the value "35.0" is too big.
669     """
670     (min_val, max_val) = _is_num_param(
671     ('min', 'max'), (min, max), to_float=True)
672     if not isinstance(value, (int, long, float, StringTypes)):
673     raise VdtTypeError(value)
674     if not isinstance(value, float):
675     # if it's a string - does it represent a float ?
676     try:
677     value = float(value)
678     except ValueError:
679     raise VdtTypeError(value)
680     if (min_val is not None) and (value < min_val):
681     raise VdtValueTooSmallError(value)
682     if (max_val is not None) and (value > max_val):
683     raise VdtValueTooBigError(value)
684     return value
685    
686     bool_dict = {
687     True: True, 'on': True, '1': True, 'true': True, 'yes': True,
688     False: False, 'off': False, '0': False, 'false': False, 'no': False,
689     }
690    
691     def is_bool(value):
692     """
693     Check if the value represents a boolean.
694    
695     >>> vtor.check('boolean', 0)
696     0
697     >>> vtor.check('boolean', False)
698     0
699     >>> vtor.check('boolean', '0')
700     0
701     >>> vtor.check('boolean', 'off')
702     0
703     >>> vtor.check('boolean', 'false')
704     0
705     >>> vtor.check('boolean', 'no')
706     0
707     >>> vtor.check('boolean', 'nO')
708     0
709     >>> vtor.check('boolean', 'NO')
710     0
711     >>> vtor.check('boolean', 1)
712     1
713     >>> vtor.check('boolean', True)
714     1
715     >>> vtor.check('boolean', '1')
716     1
717     >>> vtor.check('boolean', 'on')
718     1
719     >>> vtor.check('boolean', 'true')
720     1
721     >>> vtor.check('boolean', 'yes')
722     1
723     >>> vtor.check('boolean', 'Yes')
724     1
725     >>> vtor.check('boolean', 'YES')
726     1
727     >>> vtor.check('boolean', '')
728     Traceback (most recent call last):
729     VdtTypeError: the value "" is of the wrong type.
730     >>> vtor.check('boolean', 'up')
731     Traceback (most recent call last):
732     VdtTypeError: the value "up" is of the wrong type.
733    
734     """
735     if isinstance(value, StringTypes):
736     try:
737     return bool_dict[value.lower()]
738     except KeyError:
739     raise VdtTypeError(value)
740     # we do an equality test rather than an identity test
741     # this ensures Python 2.2 compatibilty
742     # and allows 0 and 1 to represent True and False
743     if value == False:
744     return False
745     elif value == True:
746     return True
747     else:
748     raise VdtTypeError(value)
749    
750    
751     def is_ip_addr(value):
752     """
753     Check that the supplied value is an Internet Protocol address, v.4,
754     represented by a dotted-quad string, i.e. '1.2.3.4'.
755    
756     >>> vtor.check('ip_addr', '1 ')
757     '1'
758     >>> vtor.check('ip_addr', ' 1.2')
759     '1.2'
760     >>> vtor.check('ip_addr', ' 1.2.3 ')
761     '1.2.3'
762     >>> vtor.check('ip_addr', '1.2.3.4')
763     '1.2.3.4'
764     >>> vtor.check('ip_addr', '0.0.0.0')
765     '0.0.0.0'
766     >>> vtor.check('ip_addr', '255.255.255.255')
767     '255.255.255.255'
768     >>> vtor.check('ip_addr', '255.255.255.256')
769     Traceback (most recent call last):
770     VdtValueError: the value "255.255.255.256" is unacceptable.
771     >>> vtor.check('ip_addr', '1.2.3.4.5')
772     Traceback (most recent call last):
773     VdtValueError: the value "1.2.3.4.5" is unacceptable.
774     >>> vtor.check('ip_addr', '1.2.3. 4')
775     Traceback (most recent call last):
776     VdtValueError: the value "1.2.3. 4" is unacceptable.
777     >>> vtor.check('ip_addr', 0)
778     Traceback (most recent call last):
779     VdtTypeError: the value "0" is of the wrong type.
780     """
781     if not isinstance(value, StringTypes):
782     raise VdtTypeError(value)
783     value = value.strip()
784     try:
785     dottedQuadToNum(value)
786     except ValueError:
787     raise VdtValueError(value)
788     return value
789    
790     def is_list(value, min=None, max=None):
791     """
792     Check that the value is a list of values.
793    
794     You can optionally specify the minimum and maximum number of members.
795    
796     It does no check on list members.
797    
798     >>> vtor.check('list', ())
799     ()
800     >>> vtor.check('list', [])
801     []
802     >>> vtor.check('list', (1, 2))
803     (1, 2)
804     >>> vtor.check('list', [1, 2])
805     [1, 2]
806     >>> vtor.check('list', '12')
807     '12'
808     >>> vtor.check('list(3)', (1, 2))
809     Traceback (most recent call last):
810     VdtValueTooShortError: the value "(1, 2)" is too short.
811     >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6))
812     Traceback (most recent call last):
813     VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long.
814     >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4))
815     (1, 2, 3, 4)
816     >>> vtor.check('list', 0)
817     Traceback (most recent call last):
818     VdtTypeError: the value "0" is of the wrong type.
819     """
820     (min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
821     try:
822     num_members = len(value)
823     except TypeError:
824     raise VdtTypeError(value)
825     if min_len is not None and num_members < min_len:
826     raise VdtValueTooShortError(value)
827     if max_len is not None and num_members > max_len:
828     raise VdtValueTooLongError(value)
829     return value
830    
831     def is_string(value, min=None, max=None):
832     """
833     Check that the supplied value is a string.
834    
835     You can optionally specify the minimum and maximum number of members.
836    
837     >>> vtor.check('string', '0')
838     '0'
839     >>> vtor.check('string', 0)
840     Traceback (most recent call last):
841     VdtTypeError: the value "0" is of the wrong type.
842     >>> vtor.check('string(2)', '12')
843     '12'
844     >>> vtor.check('string(2)', '1')
845     Traceback (most recent call last):
846     VdtValueTooShortError: the value "1" is too short.
847     >>> vtor.check('string(min=2, max=3)', '123')
848     '123'
849     >>> vtor.check('string(min=2, max=3)', '1234')
850     Traceback (most recent call last):
851     VdtValueTooLongError: the value "1234" is too long.
852     """
853     if not isinstance(value, StringTypes):
854     raise VdtTypeError(value)
855     return is_list(value, min, max)
856    
857     def is_int_list(value, min=None, max=None):
858     """
859     Check that the value is a list of integers.
860    
861     You can optionally specify the minimum and maximum number of members.
862    
863     Each list member is checked that it is an integer.
864    
865     >>> vtor.check('int_list', ())
866     []
867     >>> vtor.check('int_list', [])
868     []
869     >>> vtor.check('int_list', (1, 2))
870     [1, 2]
871     >>> vtor.check('int_list', [1, 2])
872     [1, 2]
873     >>> vtor.check('int_list', [1, 'a'])
874     Traceback (most recent call last):
875     VdtTypeError: the value "a" is of the wrong type.
876     """
877     return [is_integer(mem) for mem in is_list(value, min, max)]
878    
879     def is_bool_list(value, min=None, max=None):
880     """
881     Check that the value is a list of booleans.
882    
883     You can optionally specify the minimum and maximum number of members.
884    
885     Each list member is checked that it is a boolean.
886    
887     >>> vtor.check('bool_list', ())
888     []
889     >>> vtor.check('bool_list', [])
890     []
891     >>> check_res = vtor.check('bool_list', (True, False))
892     >>> check_res == [True, False]
893     1
894     >>> check_res = vtor.check('bool_list', [True, False])
895     >>> check_res == [True, False]
896     1
897     >>> vtor.check('bool_list', [True, 'a'])
898     Traceback (most recent call last):
899     VdtTypeError: the value "a" is of the wrong type.
900     """
901     return [is_bool(mem) for mem in is_list(value, min, max)]
902    
903     def is_float_list(value, min=None, max=None):
904     """
905     Check that the value is a list of floats.
906    
907     You can optionally specify the minimum and maximum number of members.
908    
909     Each list member is checked that it is a float.
910    
911     >>> vtor.check('float_list', ())
912     []
913     >>> vtor.check('float_list', [])
914     []
915     >>> vtor.check('float_list', (1, 2.0))
916     [1.0, 2.0]
917     >>> vtor.check('float_list', [1, 2.0])
918     [1.0, 2.0]
919     >>> vtor.check('float_list', [1, 'a'])
920     Traceback (most recent call last):
921     VdtTypeError: the value "a" is of the wrong type.
922     """
923     return [is_float(mem) for mem in is_list(value, min, max)]
924    
925     def is_string_list(value, min=None, max=None):
926     """
927     Check that the value is a list of strings.
928    
929     You can optionally specify the minimum and maximum number of members.
930    
931     Each list member is checked that it is a string.
932    
933     >>> vtor.check('string_list', ())
934     []
935     >>> vtor.check('string_list', [])
936     []
937     >>> vtor.check('string_list', ('a', 'b'))
938     ['a', 'b']
939     >>> vtor.check('string_list', ['a', 1])
940     Traceback (most recent call last):
941     VdtTypeError: the value "1" is of the wrong type.
942     """
943     return [is_string(mem) for mem in is_list(value, min, max)]
944    
945     def is_ip_addr_list(value, min=None, max=None):
946     """
947     Check that the value is a list of IP addresses.
948    
949     You can optionally specify the minimum and maximum number of members.
950    
951     Each list member is checked that it is an IP address.
952    
953     >>> vtor.check('ip_addr_list', ())
954     []
955     >>> vtor.check('ip_addr_list', [])
956     []
957     >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8'))
958     ['1.2.3.4', '5.6.7.8']
959     >>> vtor.check('ip_addr_list', ['a'])
960     Traceback (most recent call last):
961     VdtValueError: the value "a" is unacceptable.
962     """
963     return [is_ip_addr(mem) for mem in is_list(value, min, max)]
964    
965     fun_dict = {
966     'integer': is_integer,
967     'float': is_float,
968     'ip_addr': is_ip_addr,
969     'string': is_string,
970     'boolean': is_bool,
971     }
972    
973     def is_mixed_list(value, *args):
974     """
975     Check that the value is a list.
976     Allow specifying the type of each member.
977     Work on lists of specific lengths.
978    
979     You specify each member as a positional argument specifying type
980    
981     Each type should be one of the following strings :
982     'integer', 'float', 'ip_addr', 'string', 'boolean'
983    
984     So you can specify a list of two strings, followed by
985     two integers as :
986    
987     mixed_list('string', 'string', 'integer', 'integer')
988    
989     The length of the list must match the number of positional
990     arguments you supply.
991    
992     >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')"
993     >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True))
994     >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
995     1
996     >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True'))
997     >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
998     1
999     >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True))
1000     Traceback (most recent call last):
1001     VdtTypeError: the value "b" is of the wrong type.
1002     >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a'))
1003     Traceback (most recent call last):
1004     VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short.
1005     >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b'))
1006     Traceback (most recent call last):
1007     VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long.
1008     >>> vtor.check(mix_str, 0)
1009     Traceback (most recent call last):
1010     VdtTypeError: the value "0" is of the wrong type.
1011    
1012     This test requires an elaborate setup, because of a change in error string
1013     output from the interpreter between Python 2.2 and 2.3 .
1014    
1015     >>> res_seq = (
1016     ... 'passed an incorrect value "',
1017     ... 'yoda',
1018     ... '" for parameter "mixed_list".',
1019     ... )
1020     >>> if INTP_VER == (2, 2):
1021     ... res_str = "".join(res_seq)
1022     ... else:
1023     ... res_str = "'".join(res_seq)
1024     >>> try:
1025     ... vtor.check('mixed_list("yoda")', ('a'))
1026     ... except VdtParamError, err:
1027     ... str(err) == res_str
1028     1
1029     """
1030     try:
1031     length = len(value)
1032     except TypeError:
1033     raise VdtTypeError(value)
1034     if length < len(args):
1035     raise VdtValueTooShortError(value)
1036     elif length > len(args):
1037     raise VdtValueTooLongError(value)
1038     try:
1039     return [fun_dict[arg](val) for arg, val in zip(args, value)]
1040     except KeyError, e:
1041     raise VdtParamError('mixed_list', e)
1042    
1043     def is_option(value, *options):
1044     """
1045     This check matches the value to any of a set of options.
1046    
1047     >>> vtor.check('option("yoda", "jedi")', 'yoda')
1048     'yoda'
1049     >>> vtor.check('option("yoda", "jedi")', 'jed')
1050     Traceback (most recent call last):
1051     VdtValueError: the value "jed" is unacceptable.
1052     >>> vtor.check('option("yoda", "jedi")', 0)
1053     Traceback (most recent call last):
1054     VdtTypeError: the value "0" is of the wrong type.
1055     """
1056     if not isinstance(value, StringTypes):
1057     raise VdtTypeError(value)
1058     if not value in options:
1059     raise VdtValueError(value)
1060     return value
1061    
1062     if __name__ == '__main__':
1063     # run the code tests in doctest format
1064     import doctest
1065     m = sys.modules.get('__main__')
1066     globs = m.__dict__.copy()
1067     globs.update({
1068     'INTP_VER': INTP_VER,
1069     'vtor': Validator(),
1070     })
1071     doctest.testmod(m, globs=globs)
1072    
1073     """
1074     TODO
1075     ====
1076    
1077     Consider which parts of the regex stuff to put back in
1078    
1079     Can we implement a timestamp datatype ? (check DateUtil module)
1080    
1081     ISSUES
1082     ======
1083    
1084     Lists passed as function arguments need additional quotes. Ugly, could do
1085     with fixing this.
1086    
1087     If we could pull tuples out of arguments, it would be easier
1088     to specify arguments for 'mixed_lists'.
1089    
1090     CHANGELOG
1091     =========
1092    
1093     2005/08/25
1094     ----------
1095    
1096     Most errors now prefixed ``Vdt``
1097    
1098     ``VdtParamError`` no longer derives from ``VdtError``
1099    
1100     Finalised as version 0.2.0
1101    
1102     2005/08/21
1103     ----------
1104    
1105     By Nicola Larosa
1106    
1107     Removed the "length" argument for lists and strings, and related tests
1108    
1109     2005/08/16
1110     ----------
1111    
1112     By Nicola Larosa
1113    
1114     Deleted the "none" and "multiple" types and checks
1115    
1116     Added the None value for all types in Validation.check
1117    
1118     2005/08/14
1119     ----------
1120    
1121     By Michael Foord
1122    
1123     Removed timestamp.
1124    
1125     By Nicola Larosa
1126    
1127     Fixed bug in Validator.check: when a value that has a default is also
1128     specified in the config file, the default must be deleted from fun_kwargs
1129     anyway, otherwise the check function will get a spurious "default" keyword
1130     argument
1131    
1132     Added "ip_addr_list" check
1133    
1134     2005/08/13
1135     ----------
1136    
1137     By Nicola Larosa
1138    
1139     Updated comments at top
1140    
1141     2005/08/11
1142     ----------
1143    
1144     By Nicola Larosa
1145    
1146     Added test for interpreter version: raises RuntimeError if earlier than
1147     2.2
1148    
1149     Fixed last is_mixed_list test to work on Python 2.2 too
1150    
1151     2005/08/10
1152     ----------
1153    
1154     By Nicola Larosa
1155    
1156     Restored Python2.2 compatibility by avoiding usage of dict.pop
1157    
1158     2005/08/07
1159     ----------
1160    
1161     By Nicola Larosa
1162    
1163     Adjusted doctests for Python 2.2.3 compatibility, one test still fails
1164     for trivial reasons (string output delimiters)
1165    
1166     2005/08/05
1167     ----------
1168    
1169     By Michael Foord
1170    
1171     Added __version__, __all__, and __docformat__
1172    
1173     Replaced ``basestring`` with ``types.StringTypes``
1174    
1175     2005/07/28
1176     ----------
1177    
1178     By Nicola Larosa
1179    
1180     Reformatted final docstring in ReST format, indented it for easier folding
1181    
1182     2005/07/20
1183     ----------
1184    
1185     By Nicola Larosa
1186    
1187     Added an 'ip_addr' IPv4 address value check, with tests
1188    
1189     Updated the tests for mixed_list to include IP addresses
1190    
1191     Changed all references to value "tests" into value "checks", including
1192     the main Validator method, and all code tests
1193    
1194     2005/07/19
1195     ----------
1196    
1197     By Nicola Larosa
1198    
1199     Added even more code tests
1200    
1201     Refined the mixed_list check
1202    
1203     2005/07/18
1204     ----------
1205    
1206     By Nicola Larosa
1207    
1208     Introduced more VdtValueError subclasses
1209    
1210     Collapsed the ``_function_test`` and ``_function_parse`` methods into the
1211     ``check`` one
1212    
1213     Refined the value checks, using the new VdtValueError subclasses
1214    
1215     Changed "is_string" to use "is_list"
1216    
1217     Added many more code tests
1218    
1219     Changed the "bool" value type to "boolean"
1220    
1221     Some more code cleanup
1222    
1223     2005/07/17
1224     ----------
1225    
1226     By Nicola Larosa
1227    
1228     Code tests converted to doctest format and placed in the respective
1229     docstrings, so they are automatically checked, and easier to update
1230    
1231     Changed local vars "min" and "max" to "min_len", "max_len", "min_val" and
1232     "max_val", to avoid shadowing the builtin functions (but left function
1233     parameters alone)
1234    
1235     Uniformed value check function names to is_* convention
1236    
1237     ``date`` type name changed to ``timestamp``
1238    
1239     Avoided some code duplication in list check functions
1240    
1241     Some more code cleanup
1242    
1243     2005/07/09
1244     ----------
1245    
1246     Recoded the standard functions
1247    
1248     2005/07/08
1249     ----------
1250    
1251     Improved paramfinder regex
1252    
1253     Ripped out all the regex stuff, checks, and the example functions
1254     (to be replaced !)
1255    
1256     2005/07/06
1257     ----------
1258    
1259     By Nicola Larosa
1260    
1261     Code cleanup
1262     """
1263    

Managed by UCC Webmasters ViewVC Help
Powered by ViewVC 1.1.26