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


UCC Code Repository

Contents of /bunnyblog/modules/validate.py

Parent Directory Parent Directory | Revision Log Revision Log


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

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