1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 You can supply a default value (returned if no value is supplied)
117 using the default keyword argument.
118
119 You specify a list argument for default using a list constructor syntax in
120 the check : ::
121
122 checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3'))
123
124 A badly formatted set of arguments will raise a ``VdtParamError``.
125 """
126
127 __docformat__ = "restructuredtext en"
128
129 __version__ = '0.2.3'
130
131 __revision__ = '$Id: validate.py 123 2005-09-08 08:54:28Z fuzzyman $'
132
133 __all__ = (
134 '__version__',
135 'dottedQuadToNum',
136 'numToDottedQuad',
137 'ValidateError',
138 'VdtUnknownCheckError',
139 'VdtParamError',
140 'VdtTypeError',
141 'VdtValueError',
142 'VdtValueTooSmallError',
143 'VdtValueTooBigError',
144 'VdtValueTooShortError',
145 'VdtValueTooLongError',
146 'VdtMissingValue',
147 'Validator',
148 'is_integer',
149 'is_float',
150 'is_boolean',
151 'is_list',
152 'is_ip_addr',
153 'is_string',
154 'is_int_list',
155 'is_bool_list',
156 'is_float_list',
157 'is_string_list',
158 'is_ip_addr_list',
159 'is_mixed_list',
160 'is_option',
161 '__docformat__',
162 )
163
164 import sys
165 INTP_VER = sys.version_info[:2]
166 if INTP_VER < (2, 2):
167 raise RuntimeError("Python v.2.2 or later needed")
168
169 import re
170 StringTypes = (str, unicode)
171
172
173 _list_arg = re.compile(r'''
174 (?:
175 ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\(
176 (
177 (?:
178 \s*
179 (?:
180 (?:".*?")| # double quotes
181 (?:'.*?')| # single quotes
182 (?:[^'",\s\)][^,\)]*?) # unquoted
183 )
184 \s*,\s*
185 )*
186 (?:
187 (?:".*?")| # double quotes
188 (?:'.*?')| # single quotes
189 (?:[^'",\s\)][^,\)]*?) # unquoted
190 )? # last one
191 )
192 \)
193 )
194 ''', re.VERBOSE)
195
196 _list_members = re.compile(r'''
197 (
198 (?:".*?")| # double quotes
199 (?:'.*?')| # single quotes
200 (?:[^'",\s=][^,=]*?) # unquoted
201 )
202 (?:
203 (?:\s*,\s*)|(?:\s*$) # comma
204 )
205 ''', re.VERBOSE)
206
207 _paramstring = r'''
208 (?:
209 (
210 (?:
211 [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\(
212 (?:
213 \s*
214 (?:
215 (?:".*?")| # double quotes
216 (?:'.*?')| # single quotes
217 (?:[^'",\s\)][^,\)]*?) # unquoted
218 )
219 \s*,\s*
220 )*
221 (?:
222 (?:".*?")| # double quotes
223 (?:'.*?')| # single quotes
224 (?:[^'",\s\)][^,\)]*?) # unquoted
225 )? # last one
226 \)
227 )|
228 (?:
229 (?:".*?")| # double quotes
230 (?:'.*?')| # single quotes
231 (?:[^'",\s=][^,=]*?)| # unquoted
232 (?: # keyword argument
233 [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*
234 (?:
235 (?:".*?")| # double quotes
236 (?:'.*?')| # single quotes
237 (?:[^'",\s=][^,=]*?) # unquoted
238 )
239 )
240 )
241 )
242 (?:
243 (?:\s*,\s*)|(?:\s*$) # comma
244 )
245 )
246 '''
247
248 _matchstring = '^%s*' % _paramstring
249
250
251 try:
252 bool
253 except NameError:
255 """Simple boolean equivalent function. """
256 if val:
257 return 1
258 else:
259 return 0
260
262 """
263 Convert decimal dotted quad string to long integer
264
265 >>> dottedQuadToNum('1 ')
266 1L
267 >>> dottedQuadToNum(' 1.2')
268 16777218L
269 >>> dottedQuadToNum(' 1.2.3 ')
270 16908291L
271 >>> dottedQuadToNum('1.2.3.4')
272 16909060L
273 >>> dottedQuadToNum('1.2.3. 4')
274 Traceback (most recent call last):
275 ValueError: Not a good dotted-quad IP: 1.2.3. 4
276 >>> dottedQuadToNum('255.255.255.255')
277 4294967295L
278 >>> dottedQuadToNum('255.255.255.256')
279 Traceback (most recent call last):
280 ValueError: Not a good dotted-quad IP: 255.255.255.256
281 """
282
283
284 import socket, struct
285
286 try:
287 return struct.unpack('!L',
288 socket.inet_aton(ip.strip()))[0]
289 except socket.error:
290
291 if ip.strip() == '255.255.255.255':
292 return 0xFFFFFFFFL
293 else:
294 raise ValueError('Not a good dotted-quad IP: %s' % ip)
295 return
296
298 """
299 Convert long int to dotted quad string
300
301 >>> numToDottedQuad(-1L)
302 Traceback (most recent call last):
303 ValueError: Not a good numeric IP: -1
304 >>> numToDottedQuad(1L)
305 '0.0.0.1'
306 >>> numToDottedQuad(16777218L)
307 '1.0.0.2'
308 >>> numToDottedQuad(16908291L)
309 '1.2.0.3'
310 >>> numToDottedQuad(16909060L)
311 '1.2.3.4'
312 >>> numToDottedQuad(4294967295L)
313 '255.255.255.255'
314 >>> numToDottedQuad(4294967296L)
315 Traceback (most recent call last):
316 ValueError: Not a good numeric IP: 4294967296
317 """
318
319
320 import socket, struct
321
322
323 try:
324 return socket.inet_ntoa(
325 struct.pack('!L', long(num)))
326 except (socket.error, struct.error, OverflowError):
327 raise ValueError('Not a good numeric IP: %s' % num)
328
330 """
331 This error indicates that the check failed.
332 It can be the base class for more specific errors.
333
334 Any check function that fails ought to raise this error.
335 (or a subclass)
336
337 >>> raise ValidateError
338 Traceback (most recent call last):
339 ValidateError
340 """
341
343 """No value was supplied to a check that needed one."""
344
346 """An unknown check function was requested"""
347
349 """
350 >>> raise VdtUnknownCheckError('yoda')
351 Traceback (most recent call last):
352 VdtUnknownCheckError: the check "yoda" is unknown.
353 """
354 ValidateError.__init__(
355 self,
356 'the check "%s" is unknown.' % value)
357
359 """An incorrect parameter was passed"""
360
362 """
363 >>> raise VdtParamError('yoda', 'jedi')
364 Traceback (most recent call last):
365 VdtParamError: passed an incorrect value "jedi" for parameter "yoda".
366 """
367 SyntaxError.__init__(
368 self,
369 'passed an incorrect value "%s" for parameter "%s".' % (
370 value, name))
371
373 """The value supplied was of the wrong type"""
374
376 """
377 >>> raise VdtTypeError('jedi')
378 Traceback (most recent call last):
379 VdtTypeError: the value "jedi" is of the wrong type.
380 """
381 ValidateError.__init__(
382 self,
383 'the value "%s" is of the wrong type.' % value)
384
386 """
387 The value supplied was of the correct type, but was not an allowed value.
388 """
389
391 """
392 >>> raise VdtValueError('jedi')
393 Traceback (most recent call last):
394 VdtValueError: the value "jedi" is unacceptable.
395 """
396 ValidateError.__init__(
397 self,
398 'the value "%s" is unacceptable.' % value)
399
401 """The value supplied was of the correct type, but was too small."""
402
404 """
405 >>> raise VdtValueTooSmallError('0')
406 Traceback (most recent call last):
407 VdtValueTooSmallError: the value "0" is too small.
408 """
409 ValidateError.__init__(
410 self,
411 'the value "%s" is too small.' % value)
412
414 """The value supplied was of the correct type, but was too big."""
415
417 """
418 >>> raise VdtValueTooBigError('1')
419 Traceback (most recent call last):
420 VdtValueTooBigError: the value "1" is too big.
421 """
422 ValidateError.__init__(
423 self,
424 'the value "%s" is too big.' % value)
425
427 """The value supplied was of the correct type, but was too short."""
428
430 """
431 >>> raise VdtValueTooShortError('jed')
432 Traceback (most recent call last):
433 VdtValueTooShortError: the value "jed" is too short.
434 """
435 ValidateError.__init__(
436 self,
437 'the value "%s" is too short.' % (value,))
438
451
453 """
454 Validator is an object that allows you to register a set of 'checks'.
455 These checks take input and test that it conforms to the check.
456
457 This can also involve converting the value from a string into
458 the correct datatype.
459
460 The ``check`` method takes an input string which configures which
461 check is to be used and applies that check to a supplied value.
462
463 An example input string would be:
464 'int_range(param1, param2)'
465
466 You would then provide something like:
467
468 >>> def int_range_check(value, min, max):
469 ... # turn min and max from strings to integers
470 ... min = int(min)
471 ... max = int(max)
472 ... # check that value is of the correct type.
473 ... # possible valid inputs are integers or strings
474 ... # that represent integers
475 ... if not isinstance(value, (int, long, StringTypes)):
476 ... raise VdtTypeError(value)
477 ... elif isinstance(value, StringTypes):
478 ... # if we are given a string
479 ... # attempt to convert to an integer
480 ... try:
481 ... value = int(value)
482 ... except ValueError:
483 ... raise VdtValueError(value)
484 ... # check the value is between our constraints
485 ... if not min <= value:
486 ... raise VdtValueTooSmallError(value)
487 ... if not value <= max:
488 ... raise VdtValueTooBigError(value)
489 ... return value
490
491 >>> fdict = {'int_range': int_range_check}
492 >>> vtr1 = Validator(fdict)
493 >>> vtr1.check('int_range(20, 40)', '30')
494 30
495 >>> vtr1.check('int_range(20, 40)', '60')
496 Traceback (most recent call last):
497 VdtValueTooBigError: the value "60" is too big.
498
499 New functions can be added with : ::
500
501 >>> vtr2 = Validator()
502 >>> vtr2.functions['int_range'] = int_range_check
503
504 Or by passing in a dictionary of functions when Validator
505 is instantiated.
506
507 Your functions *can* use keyword arguments,
508 but the first argument should always be 'value'.
509
510 If the function doesn't take additional arguments,
511 the parentheses are optional in the check.
512 It can be written with either of : ::
513
514 keyword = function_name
515 keyword = function_name()
516
517 The first program to utilise Validator() was Michael Foord's
518 ConfigObj, an alternative to ConfigParser which supports lists and
519 can validate a config file using a config schema.
520 For more details on using Validator with ConfigObj see:
521 http://www.voidspace.org.uk/python/configobj.html
522 """
523
524
525 _func_re = re.compile(r'(.+?)\((.*)\)')
526
527
528 _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$')
529
530
531
532 _list_arg = _list_arg
533
534
535 _list_members = _list_members
536
537
538
539 _paramfinder = re.compile(_paramstring, re.VERBOSE)
540 _matchfinder = re.compile(_matchstring, re.VERBOSE)
541
542
544 """
545 >>> vtri = Validator()
546 """
547 self.functions = {
548 '': self._pass,
549 'integer': is_integer,
550 'float': is_float,
551 'boolean': is_boolean,
552 'ip_addr': is_ip_addr,
553 'string': is_string,
554 'list': is_list,
555 'int_list': is_int_list,
556 'float_list': is_float_list,
557 'bool_list': is_bool_list,
558 'ip_addr_list': is_ip_addr_list,
559 'string_list': is_string_list,
560 'mixed_list': is_mixed_list,
561 'pass': self._pass,
562 'option': is_option,
563 }
564 if functions is not None:
565 self.functions.update(functions)
566
567 self.baseErrorClass = ValidateError
568
569 - def check(self, check, value, missing=False):
570 """
571 Usage: check(check, value)
572
573 Arguments:
574 check: string representing check to apply (including arguments)
575 value: object to be checked
576 Returns value, converted to correct type if necessary
577
578 If the check fails, raises a ``ValidateError`` subclass.
579
580 >>> vtor.check('yoda', '')
581 Traceback (most recent call last):
582 VdtUnknownCheckError: the check "yoda" is unknown.
583 >>> vtor.check('yoda()', '')
584 Traceback (most recent call last):
585 VdtUnknownCheckError: the check "yoda" is unknown.
586 """
587 fun_match = self._func_re.match(check)
588 if fun_match:
589 fun_name = fun_match.group(1)
590 arg_string = fun_match.group(2)
591 arg_match = self._matchfinder.match(arg_string)
592 if arg_match is None:
593
594 raise VdtParamError
595 fun_args = []
596 fun_kwargs = {}
597
598 for arg in self._paramfinder.findall(arg_string):
599
600 arg = arg.strip()
601 listmatch = self._list_arg.match(arg)
602 if listmatch:
603 key, val = self._list_handle(listmatch)
604 fun_kwargs[key] = val
605 continue
606 keymatch = self._key_arg.match(arg)
607 if keymatch:
608 val = self._unquote(keymatch.group(2))
609 fun_kwargs[keymatch.group(1)] = val
610 continue
611
612 fun_args.append(self._unquote(arg))
613 else:
614
615 (fun_name, fun_args, fun_kwargs) = (check, (), {})
616
617 if missing:
618 try:
619 value = fun_kwargs['default']
620 except KeyError:
621 raise VdtMissingValue
622 if value == 'None':
623 value = None
624 if value is None:
625 return None
626
627
628 try:
629 del fun_kwargs['default']
630 except KeyError:
631 pass
632 try:
633 fun = self.functions[fun_name]
634 except KeyError:
635 raise VdtUnknownCheckError(fun_name)
636 else:
637
638
639 return fun(value, *fun_args, **fun_kwargs)
640
642 """Unquote a value if necessary."""
643 if (len(val) > 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]):
644 val = val[1:-1]
645 return val
646
648 """Take apart a ``keyword=list('val, 'val')`` type string."""
649 out = []
650 name = listmatch.group(1)
651 args = listmatch.group(2)
652 for arg in self._list_members.findall(args):
653 out.append(self._unquote(arg))
654 return name, out
655
657 """
658 Dummy check that always passes
659
660 >>> vtor.check('', 0)
661 0
662 >>> vtor.check('', '0')
663 '0'
664 """
665 return value
666
667
669 """
670 Return numbers from inputs or raise VdtParamError.
671
672 Lets ``None`` pass through.
673 Pass in keyword argument ``to_float=True`` to
674 use float for the conversion rather than int.
675
676 >>> _is_num_param(('', ''), (0, 1.0))
677 [0, 1]
678 >>> _is_num_param(('', ''), (0, 1.0), to_float=True)
679 [0.0, 1.0]
680 >>> _is_num_param(('a'), ('a'))
681 Traceback (most recent call last):
682 VdtParamError: passed an incorrect value "a" for parameter "a".
683 """
684 fun = to_float and float or int
685 out_params = []
686 for (name, val) in zip(names, values):
687 if val is None:
688 out_params.append(val)
689 elif isinstance(val, (int, long, float, StringTypes)):
690 try:
691 out_params.append(fun(val))
692 except ValueError, e:
693 raise VdtParamError(name, val)
694 else:
695 raise VdtParamError(name, val)
696 return out_params
697
698
699
700
701
702
703
705 """
706 A check that tests that a given value is an integer (int, or long)
707 and optionally, between bounds. A negative value is accepted, while
708 a float will fail.
709
710 If the value is a string, then the conversion is done - if possible.
711 Otherwise a VdtError is raised.
712
713 >>> vtor.check('integer', '-1')
714 -1
715 >>> vtor.check('integer', '0')
716 0
717 >>> vtor.check('integer', 9)
718 9
719 >>> vtor.check('integer', 'a')
720 Traceback (most recent call last):
721 VdtTypeError: the value "a" is of the wrong type.
722 >>> vtor.check('integer', '2.2')
723 Traceback (most recent call last):
724 VdtTypeError: the value "2.2" is of the wrong type.
725 >>> vtor.check('integer(10)', '20')
726 20
727 >>> vtor.check('integer(max=20)', '15')
728 15
729 >>> vtor.check('integer(10)', '9')
730 Traceback (most recent call last):
731 VdtValueTooSmallError: the value "9" is too small.
732 >>> vtor.check('integer(10)', 9)
733 Traceback (most recent call last):
734 VdtValueTooSmallError: the value "9" is too small.
735 >>> vtor.check('integer(max=20)', '35')
736 Traceback (most recent call last):
737 VdtValueTooBigError: the value "35" is too big.
738 >>> vtor.check('integer(max=20)', 35)
739 Traceback (most recent call last):
740 VdtValueTooBigError: the value "35" is too big.
741 >>> vtor.check('integer(0, 9)', False)
742 0
743 """
744
745 (min_val, max_val) = _is_num_param(('min', 'max'), (min, max))
746 if not isinstance(value, (int, long, StringTypes)):
747 raise VdtTypeError(value)
748 if isinstance(value, StringTypes):
749
750 try:
751 value = int(value)
752 except ValueError:
753 raise VdtTypeError(value)
754 if (min_val is not None) and (value < min_val):
755 raise VdtValueTooSmallError(value)
756 if (max_val is not None) and (value > max_val):
757 raise VdtValueTooBigError(value)
758 return value
759
760 -def is_float(value, min=None, max=None):
761 """
762 A check that tests that a given value is a float
763 (an integer will be accepted), and optionally - that it is between bounds.
764
765 If the value is a string, then the conversion is done - if possible.
766 Otherwise a VdtError is raised.
767
768 This can accept negative values.
769
770 >>> vtor.check('float', '2')
771 2.0
772
773 From now on we multiply the value to avoid comparing decimals
774
775 >>> vtor.check('float', '-6.8') * 10
776 -68.0
777 >>> vtor.check('float', '12.2') * 10
778 122.0
779 >>> vtor.check('float', 8.4) * 10
780 84.0
781 >>> vtor.check('float', 'a')
782 Traceback (most recent call last):
783 VdtTypeError: the value "a" is of the wrong type.
784 >>> vtor.check('float(10.1)', '10.2') * 10
785 102.0
786 >>> vtor.check('float(max=20.2)', '15.1') * 10
787 151.0
788 >>> vtor.check('float(10.0)', '9.0')
789 Traceback (most recent call last):
790 VdtValueTooSmallError: the value "9.0" is too small.
791 >>> vtor.check('float(max=20.0)', '35.0')
792 Traceback (most recent call last):
793 VdtValueTooBigError: the value "35.0" is too big.
794 """
795 (min_val, max_val) = _is_num_param(
796 ('min', 'max'), (min, max), to_float=True)
797 if not isinstance(value, (int, long, float, StringTypes)):
798 raise VdtTypeError(value)
799 if not isinstance(value, float):
800
801 try:
802 value = float(value)
803 except ValueError:
804 raise VdtTypeError(value)
805 if (min_val is not None) and (value < min_val):
806 raise VdtValueTooSmallError(value)
807 if (max_val is not None) and (value > max_val):
808 raise VdtValueTooBigError(value)
809 return value
810
811 bool_dict = {
812 True: True, 'on': True, '1': True, 'true': True, 'yes': True,
813 False: False, 'off': False, '0': False, 'false': False, 'no': False,
814 }
815
817 """
818 Check if the value represents a boolean.
819
820 >>> vtor.check('boolean', 0)
821 0
822 >>> vtor.check('boolean', False)
823 0
824 >>> vtor.check('boolean', '0')
825 0
826 >>> vtor.check('boolean', 'off')
827 0
828 >>> vtor.check('boolean', 'false')
829 0
830 >>> vtor.check('boolean', 'no')
831 0
832 >>> vtor.check('boolean', 'nO')
833 0
834 >>> vtor.check('boolean', 'NO')
835 0
836 >>> vtor.check('boolean', 1)
837 1
838 >>> vtor.check('boolean', True)
839 1
840 >>> vtor.check('boolean', '1')
841 1
842 >>> vtor.check('boolean', 'on')
843 1
844 >>> vtor.check('boolean', 'true')
845 1
846 >>> vtor.check('boolean', 'yes')
847 1
848 >>> vtor.check('boolean', 'Yes')
849 1
850 >>> vtor.check('boolean', 'YES')
851 1
852 >>> vtor.check('boolean', '')
853 Traceback (most recent call last):
854 VdtTypeError: the value "" is of the wrong type.
855 >>> vtor.check('boolean', 'up')
856 Traceback (most recent call last):
857 VdtTypeError: the value "up" is of the wrong type.
858
859 """
860 if isinstance(value, StringTypes):
861 try:
862 return bool_dict[value.lower()]
863 except KeyError:
864 raise VdtTypeError(value)
865
866
867
868 if value == False:
869 return False
870 elif value == True:
871 return True
872 else:
873 raise VdtTypeError(value)
874
875
877 """
878 Check that the supplied value is an Internet Protocol address, v.4,
879 represented by a dotted-quad string, i.e. '1.2.3.4'.
880
881 >>> vtor.check('ip_addr', '1 ')
882 '1'
883 >>> vtor.check('ip_addr', ' 1.2')
884 '1.2'
885 >>> vtor.check('ip_addr', ' 1.2.3 ')
886 '1.2.3'
887 >>> vtor.check('ip_addr', '1.2.3.4')
888 '1.2.3.4'
889 >>> vtor.check('ip_addr', '0.0.0.0')
890 '0.0.0.0'
891 >>> vtor.check('ip_addr', '255.255.255.255')
892 '255.255.255.255'
893 >>> vtor.check('ip_addr', '255.255.255.256')
894 Traceback (most recent call last):
895 VdtValueError: the value "255.255.255.256" is unacceptable.
896 >>> vtor.check('ip_addr', '1.2.3.4.5')
897 Traceback (most recent call last):
898 VdtValueError: the value "1.2.3.4.5" is unacceptable.
899 >>> vtor.check('ip_addr', '1.2.3. 4')
900 Traceback (most recent call last):
901 VdtValueError: the value "1.2.3. 4" is unacceptable.
902 >>> vtor.check('ip_addr', 0)
903 Traceback (most recent call last):
904 VdtTypeError: the value "0" is of the wrong type.
905 """
906 if not isinstance(value, StringTypes):
907 raise VdtTypeError(value)
908 value = value.strip()
909 try:
910 dottedQuadToNum(value)
911 except ValueError:
912 raise VdtValueError(value)
913 return value
914
915 -def is_list(value, min=None, max=None):
916 """
917 Check that the value is a list of values.
918
919 You can optionally specify the minimum and maximum number of members.
920
921 It does no check on list members.
922
923 >>> vtor.check('list', ())
924 ()
925 >>> vtor.check('list', [])
926 []
927 >>> vtor.check('list', (1, 2))
928 (1, 2)
929 >>> vtor.check('list', [1, 2])
930 [1, 2]
931 >>> vtor.check('list', '12')
932 '12'
933 >>> vtor.check('list(3)', (1, 2))
934 Traceback (most recent call last):
935 VdtValueTooShortError: the value "(1, 2)" is too short.
936 >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6))
937 Traceback (most recent call last):
938 VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long.
939 >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4))
940 (1, 2, 3, 4)
941 >>> vtor.check('list', 0)
942 Traceback (most recent call last):
943 VdtTypeError: the value "0" is of the wrong type.
944 """
945 (min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
946 try:
947 num_members = len(value)
948 except TypeError:
949 raise VdtTypeError(value)
950 if min_len is not None and num_members < min_len:
951 raise VdtValueTooShortError(value)
952 if max_len is not None and num_members > max_len:
953 raise VdtValueTooLongError(value)
954 return value
955
957 """
958 Check that the supplied value is a string.
959
960 You can optionally specify the minimum and maximum number of members.
961
962 >>> vtor.check('string', '0')
963 '0'
964 >>> vtor.check('string', 0)
965 Traceback (most recent call last):
966 VdtTypeError: the value "0" is of the wrong type.
967 >>> vtor.check('string(2)', '12')
968 '12'
969 >>> vtor.check('string(2)', '1')
970 Traceback (most recent call last):
971 VdtValueTooShortError: the value "1" is too short.
972 >>> vtor.check('string(min=2, max=3)', '123')
973 '123'
974 >>> vtor.check('string(min=2, max=3)', '1234')
975 Traceback (most recent call last):
976 VdtValueTooLongError: the value "1234" is too long.
977 """
978 if not isinstance(value, StringTypes):
979 raise VdtTypeError(value)
980 (min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
981 try:
982 num_members = len(value)
983 except TypeError:
984 raise VdtTypeError(value)
985 if min_len is not None and num_members < min_len:
986 raise VdtValueTooShortError(value)
987 if max_len is not None and num_members > max_len:
988 raise VdtValueTooLongError(value)
989 return value
990
992 """
993 Check that the value is a list of integers.
994
995 You can optionally specify the minimum and maximum number of members.
996
997 Each list member is checked that it is an integer.
998
999 >>> vtor.check('int_list', ())
1000 []
1001 >>> vtor.check('int_list', [])
1002 []
1003 >>> vtor.check('int_list', (1, 2))
1004 [1, 2]
1005 >>> vtor.check('int_list', [1, 2])
1006 [1, 2]
1007 >>> vtor.check('int_list', [1, 'a'])
1008 Traceback (most recent call last):
1009 VdtTypeError: the value "a" is of the wrong type.
1010 """
1011 return [is_integer(mem) for mem in is_list(value, min, max)]
1012
1014 """
1015 Check that the value is a list of booleans.
1016
1017 You can optionally specify the minimum and maximum number of members.
1018
1019 Each list member is checked that it is a boolean.
1020
1021 >>> vtor.check('bool_list', ())
1022 []
1023 >>> vtor.check('bool_list', [])
1024 []
1025 >>> check_res = vtor.check('bool_list', (True, False))
1026 >>> check_res == [True, False]
1027 1
1028 >>> check_res = vtor.check('bool_list', [True, False])
1029 >>> check_res == [True, False]
1030 1
1031 >>> vtor.check('bool_list', [True, 'a'])
1032 Traceback (most recent call last):
1033 VdtTypeError: the value "a" is of the wrong type.
1034 """
1035 return [is_boolean(mem) for mem in is_list(value, min, max)]
1036
1038 """
1039 Check that the value is a list of floats.
1040
1041 You can optionally specify the minimum and maximum number of members.
1042
1043 Each list member is checked that it is a float.
1044
1045 >>> vtor.check('float_list', ())
1046 []
1047 >>> vtor.check('float_list', [])
1048 []
1049 >>> vtor.check('float_list', (1, 2.0))
1050 [1.0, 2.0]
1051 >>> vtor.check('float_list', [1, 2.0])
1052 [1.0, 2.0]
1053 >>> vtor.check('float_list', [1, 'a'])
1054 Traceback (most recent call last):
1055 VdtTypeError: the value "a" is of the wrong type.
1056 """
1057 return [is_float(mem) for mem in is_list(value, min, max)]
1058
1060 """
1061 Check that the value is a list of strings.
1062
1063 You can optionally specify the minimum and maximum number of members.
1064
1065 Each list member is checked that it is a string.
1066
1067 >>> vtor.check('string_list', ())
1068 []
1069 >>> vtor.check('string_list', [])
1070 []
1071 >>> vtor.check('string_list', ('a', 'b'))
1072 ['a', 'b']
1073 >>> vtor.check('string_list', ['a', 1])
1074 Traceback (most recent call last):
1075 VdtTypeError: the value "1" is of the wrong type.
1076 >>> vtor.check('string_list', 'hello')
1077 Traceback (most recent call last):
1078 VdtTypeError: the value "hello" is of the wrong type.
1079 """
1080 if isinstance(value, StringTypes):
1081 raise VdtTypeError(value)
1082 return [is_string(mem) for mem in is_list(value, min, max)]
1083
1085 """
1086 Check that the value is a list of IP addresses.
1087
1088 You can optionally specify the minimum and maximum number of members.
1089
1090 Each list member is checked that it is an IP address.
1091
1092 >>> vtor.check('ip_addr_list', ())
1093 []
1094 >>> vtor.check('ip_addr_list', [])
1095 []
1096 >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8'))
1097 ['1.2.3.4', '5.6.7.8']
1098 >>> vtor.check('ip_addr_list', ['a'])
1099 Traceback (most recent call last):
1100 VdtValueError: the value "a" is unacceptable.
1101 """
1102 return [is_ip_addr(mem) for mem in is_list(value, min, max)]
1103
1104 fun_dict = {
1105 'integer': is_integer,
1106 'float': is_float,
1107 'ip_addr': is_ip_addr,
1108 'string': is_string,
1109 'boolean': is_boolean,
1110 }
1111
1113 """
1114 Check that the value is a list.
1115 Allow specifying the type of each member.
1116 Work on lists of specific lengths.
1117
1118 You specify each member as a positional argument specifying type
1119
1120 Each type should be one of the following strings :
1121 'integer', 'float', 'ip_addr', 'string', 'boolean'
1122
1123 So you can specify a list of two strings, followed by
1124 two integers as :
1125
1126 mixed_list('string', 'string', 'integer', 'integer')
1127
1128 The length of the list must match the number of positional
1129 arguments you supply.
1130
1131 >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')"
1132 >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True))
1133 >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
1134 1
1135 >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True'))
1136 >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
1137 1
1138 >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True))
1139 Traceback (most recent call last):
1140 VdtTypeError: the value "b" is of the wrong type.
1141 >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a'))
1142 Traceback (most recent call last):
1143 VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short.
1144 >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b'))
1145 Traceback (most recent call last):
1146 VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long.
1147 >>> vtor.check(mix_str, 0)
1148 Traceback (most recent call last):
1149 VdtTypeError: the value "0" is of the wrong type.
1150
1151 This test requires an elaborate setup, because of a change in error string
1152 output from the interpreter between Python 2.2 and 2.3 .
1153
1154 >>> res_seq = (
1155 ... 'passed an incorrect value "',
1156 ... 'yoda',
1157 ... '" for parameter "mixed_list".',
1158 ... )
1159 >>> if INTP_VER == (2, 2):
1160 ... res_str = "".join(res_seq)
1161 ... else:
1162 ... res_str = "'".join(res_seq)
1163 >>> try:
1164 ... vtor.check('mixed_list("yoda")', ('a'))
1165 ... except VdtParamError, err:
1166 ... str(err) == res_str
1167 1
1168 """
1169 try:
1170 length = len(value)
1171 except TypeError:
1172 raise VdtTypeError(value)
1173 if length < len(args):
1174 raise VdtValueTooShortError(value)
1175 elif length > len(args):
1176 raise VdtValueTooLongError(value)
1177 try:
1178 return [fun_dict[arg](val) for arg, val in zip(args, value)]
1179 except KeyError, e:
1180 raise VdtParamError('mixed_list', e)
1181
1183 """
1184 This check matches the value to any of a set of options.
1185
1186 >>> vtor.check('option("yoda", "jedi")', 'yoda')
1187 'yoda'
1188 >>> vtor.check('option("yoda", "jedi")', 'jed')
1189 Traceback (most recent call last):
1190 VdtValueError: the value "jed" is unacceptable.
1191 >>> vtor.check('option("yoda", "jedi")', 0)
1192 Traceback (most recent call last):
1193 VdtTypeError: the value "0" is of the wrong type.
1194 """
1195 if not isinstance(value, StringTypes):
1196 raise VdtTypeError(value)
1197 if not value in options:
1198 raise VdtValueError(value)
1199 return value
1200
1201 -def _test(value, *args, **keywargs):
1202 """
1203 A function that exists for test purposes.
1204
1205 >>> checks = [
1206 ... '3, 6, min=1, max=3, test=list(a, b, c)',
1207 ... '3',
1208 ... '3, 6',
1209 ... '3,',
1210 ... 'min=1, test="a b c"',
1211 ... 'min=5, test="a, b, c"',
1212 ... 'min=1, max=3, test="a, b, c"',
1213 ... 'min=-100, test=-99',
1214 ... 'min=1, max=3',
1215 ... '3, 6, test="36"',
1216 ... '3, 6, test="a, b, c"',
1217 ... '3, max=3, test=list("a", "b", "c")',
1218 ... '''3, max=3, test=list("'a'", 'b', "x=(c)")''',
1219 ... "test='x=fish(3)'",
1220 ... ]
1221 >>> v = Validator({'test': _test})
1222 >>> for entry in checks:
1223 ... print v.check(('test(%s)' % entry), 3)
1224 (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'})
1225 (3, ('3',), {})
1226 (3, ('3', '6'), {})
1227 (3, ('3',), {})
1228 (3, (), {'test': 'a b c', 'min': '1'})
1229 (3, (), {'test': 'a, b, c', 'min': '5'})
1230 (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'})
1231 (3, (), {'test': '-99', 'min': '-100'})
1232 (3, (), {'max': '3', 'min': '1'})
1233 (3, ('3', '6'), {'test': '36'})
1234 (3, ('3', '6'), {'test': 'a, b, c'})
1235 (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'})
1236 (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'})
1237 (3, (), {'test': 'x=fish(3)'})
1238 """
1239 return (value, args, keywargs)
1240
1241
1242 if __name__ == '__main__':
1243
1244 import doctest
1245 m = sys.modules.get('__main__')
1246 globs = m.__dict__.copy()
1247 globs.update({
1248 'INTP_VER': INTP_VER,
1249 'vtor': Validator(),
1250 })
1251 doctest.testmod(m, globs=globs)
1252
1253 """
1254 TODO
1255 ====
1256
1257 Consider which parts of the regex stuff to put back in
1258
1259 Can we implement a timestamp datatype ? (check DateUtil module)
1260
1261 ISSUES
1262 ======
1263
1264 If we could pull tuples out of arguments, it would be easier
1265 to specify arguments for 'mixed_lists'.
1266
1267 CHANGELOG
1268 =========
1269
1270 2006/12/17
1271 ----------
1272
1273 By Nicola Larosa
1274
1275 Fixed validate doc to talk of ``boolean`` instead of ``bool``, changed the
1276 ``is_bool`` function to ``is_boolean`` (Sourceforge bug #1531525).
1277
1278 2006/04/23
1279 ----------
1280
1281 Addressed bug where a string would pass the ``is_list`` test. (Thanks to
1282 Konrad Wojas.)
1283
1284 2005/12/16
1285 ----------
1286
1287 Fixed bug so we can handle keyword argument values with commas.
1288
1289 We now use a list constructor for passing list values to keyword arguments
1290 (including ``default``) : ::
1291
1292 default=list("val", "val", "val")
1293
1294 Added the ``_test`` test. {sm;:-)}
1295
1296 0.2.1
1297
1298 2005/12/12
1299 ----------
1300
1301 Moved a function call outside a try...except block.
1302
1303 2005/08/25
1304 ----------
1305
1306 Most errors now prefixed ``Vdt``
1307
1308 ``VdtParamError`` no longer derives from ``VdtError``
1309
1310 Finalised as version 0.2.0
1311
1312 2005/08/21
1313 ----------
1314
1315 By Nicola Larosa
1316
1317 Removed the "length" argument for lists and strings, and related tests
1318
1319 2005/08/16
1320 ----------
1321
1322 By Nicola Larosa
1323
1324 Deleted the "none" and "multiple" types and checks
1325
1326 Added the None value for all types in Validation.check
1327
1328 2005/08/14
1329 ----------
1330
1331 By Michael Foord
1332
1333 Removed timestamp.
1334
1335 By Nicola Larosa
1336
1337 Fixed bug in Validator.check: when a value that has a default is also
1338 specified in the config file, the default must be deleted from fun_kwargs
1339 anyway, otherwise the check function will get a spurious "default" keyword
1340 argument
1341
1342 Added "ip_addr_list" check
1343
1344 2005/08/13
1345 ----------
1346
1347 By Nicola Larosa
1348
1349 Updated comments at top
1350
1351 2005/08/11
1352 ----------
1353
1354 By Nicola Larosa
1355
1356 Added test for interpreter version: raises RuntimeError if earlier than
1357 2.2
1358
1359 Fixed last is_mixed_list test to work on Python 2.2 too
1360
1361 2005/08/10
1362 ----------
1363
1364 By Nicola Larosa
1365
1366 Restored Python2.2 compatibility by avoiding usage of dict.pop
1367
1368 2005/08/07
1369 ----------
1370
1371 By Nicola Larosa
1372
1373 Adjusted doctests for Python 2.2.3 compatibility, one test still fails
1374 for trivial reasons (string output delimiters)
1375
1376 2005/08/05
1377 ----------
1378
1379 By Michael Foord
1380
1381 Added __version__, __all__, and __docformat__
1382
1383 Replaced ``basestring`` with ``types.StringTypes``
1384
1385 2005/07/28
1386 ----------
1387
1388 By Nicola Larosa
1389
1390 Reformatted final docstring in ReST format, indented it for easier folding
1391
1392 2005/07/20
1393 ----------
1394
1395 By Nicola Larosa
1396
1397 Added an 'ip_addr' IPv4 address value check, with tests
1398
1399 Updated the tests for mixed_list to include IP addresses
1400
1401 Changed all references to value "tests" into value "checks", including
1402 the main Validator method, and all code tests
1403
1404 2005/07/19
1405 ----------
1406
1407 By Nicola Larosa
1408
1409 Added even more code tests
1410
1411 Refined the mixed_list check
1412
1413 2005/07/18
1414 ----------
1415
1416 By Nicola Larosa
1417
1418 Introduced more VdtValueError subclasses
1419
1420 Collapsed the ``_function_test`` and ``_function_parse`` methods into the
1421 ``check`` one
1422
1423 Refined the value checks, using the new VdtValueError subclasses
1424
1425 Changed "is_string" to use "is_list"
1426
1427 Added many more code tests
1428
1429 Changed the "bool" value type to "boolean"
1430
1431 Some more code cleanup
1432
1433 2005/07/17
1434 ----------
1435
1436 By Nicola Larosa
1437
1438 Code tests converted to doctest format and placed in the respective
1439 docstrings, so they are automatically checked, and easier to update
1440
1441 Changed local vars "min" and "max" to "min_len", "max_len", "min_val" and
1442 "max_val", to avoid shadowing the builtin functions (but left function
1443 parameters alone)
1444
1445 Uniformed value check function names to is_* convention
1446
1447 ``date`` type name changed to ``timestamp``
1448
1449 Avoided some code duplication in list check functions
1450
1451 Some more code cleanup
1452
1453 2005/07/09
1454 ----------
1455
1456 Recoded the standard functions
1457
1458 2005/07/08
1459 ----------
1460
1461 Improved paramfinder regex
1462
1463 Ripped out all the regex stuff, checks, and the example functions
1464 (to be replaced !)
1465
1466 2005/07/06
1467 ----------
1468
1469 By Nicola Larosa
1470
1471 Code cleanup
1472 """
1473