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
8
# This software is licensed under the terms of the BSD license.
9
# http://www.voidspace.org.uk/python/license.shtml
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.
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.
20
The Validator object is used to check that supplied values
21
conform to a specification.
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.
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
35
The standard functions work with the following basic data types :
43
plus lists of these datatypes
45
Adding additional checks is done through coding simple functions.
47
The full set of standard checks are :
49
* 'integer': matches integer values (including negative)
50
Takes optional 'min' and 'max' arguments : ::
53
integer(3, 9) # any value from 3 to 9
54
integer(min=0) # any positive value
57
* 'float': matches float values
58
Has the same parameters as the integer check.
60
* 'boolean': matches boolean values - ``True`` or ``False``
61
Acceptable string values for True are :
63
Acceptable string values for False are :
66
Any other value raises an error.
68
* 'ip_addr': matches an Internet Protocol address, v.4, represented
69
by a dotted-quad string, i.e. '1.2.3.4'.
71
* 'string': matches any string.
72
Takes optional keyword args 'min' and 'max'
73
to specify min and max lengths of the string.
75
* 'list': matches any list.
76
Takes optional keyword args 'min', and 'max' to specify min and
77
max sizes of the list.
79
* 'int_list': Matches a list of integers.
80
Takes the same arguments as list.
82
* 'float_list': Matches a list of floats.
83
Takes the same arguments as list.
85
* 'bool_list': Matches a list of boolean values.
86
Takes the same arguments as list.
88
* 'ip_addr_list': Matches a list of IP addresses.
89
Takes the same arguments as list.
91
* 'string_list': Matches a list of strings.
92
Takes the same arguments as list.
94
* 'mixed_list': Matches a list with different types in
95
specific positions. List size must match
96
the number of arguments.
98
Each position can be one of :
99
'integer', 'float', 'ip_addr', 'string', 'boolean'
101
So to specify a list with two strings followed
102
by two integers, you write the check as : ::
104
mixed_list('string', 'string', 'integer', 'integer')
106
* 'pass': This check matches everything ! It never fails
107
and the value is unchanged.
109
It is also the default if no check is specified.
111
* 'option': This check matches any from a list of options.
112
You specify this check with : ::
114
option('option 1', 'option 2', 'option 3')
116
You can supply a default value (returned if no value is supplied)
117
using the default keyword argument.
119
You specify a list argument for default using a list constructor syntax in
122
checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3'))
124
A badly formatted set of arguments will raise a ``VdtParamError``.
127
__docformat__ = "restructuredtext en"
129
__version__ = '0.2.1'
131
__revision__ = '$Id: validate.py 123 2005-09-08 08:54:28Z fuzzyman $'
138
'VdtUnknownCheckError',
142
'VdtValueTooSmallError',
143
'VdtValueTooBigError',
144
'VdtValueTooShortError',
145
'VdtValueTooLongError',
165
INTP_VER = sys.version_info[:2]
166
if INTP_VER < (2, 2):
167
raise RuntimeError("Python v.2.2 or later needed")
170
StringTypes = (str, unicode)
173
_list_arg = re.compile(r'''
175
([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\(
180
(?:".*?")| # double quotes
181
(?:'.*?')| # single quotes
182
(?:[^'",\s\)][^,\)]*?) # unquoted
187
(?:".*?")| # double quotes
188
(?:'.*?')| # single quotes
189
(?:[^'",\s\)][^,\)]*?) # unquoted
194
''', re.VERBOSE) # two groups
196
_list_members = re.compile(r'''
198
(?:".*?")| # double quotes
199
(?:'.*?')| # single quotes
200
(?:[^'",\s=][^,=]*?) # unquoted
203
(?:\s*,\s*)|(?:\s*$) # comma
205
''', re.VERBOSE) # one group
211
[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\(
215
(?:".*?")| # double quotes
216
(?:'.*?')| # single quotes
217
(?:[^'",\s\)][^,\)]*?) # unquoted
222
(?:".*?")| # double quotes
223
(?:'.*?')| # single quotes
224
(?:[^'",\s\)][^,\)]*?) # unquoted
229
(?:".*?")| # double quotes
230
(?:'.*?')| # single quotes
231
(?:[^'",\s=][^,=]*?)| # unquoted
232
(?: # keyword argument
233
[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*
235
(?:".*?")| # double quotes
236
(?:'.*?')| # single quotes
237
(?:[^'",\s=][^,=]*?) # unquoted
243
(?:\s*,\s*)|(?:\s*$) # comma
248
_matchstring = '^%s*' % _paramstring
250
# Python pre 2.2.1 doesn't have bool
255
"""Simple boolean equivalent function. """
261
def dottedQuadToNum(ip):
263
Convert decimal dotted quad string to long integer
265
>>> dottedQuadToNum('1 ')
267
>>> dottedQuadToNum(' 1.2')
269
>>> dottedQuadToNum(' 1.2.3 ')
271
>>> dottedQuadToNum('1.2.3.4')
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')
278
>>> dottedQuadToNum('255.255.255.256')
279
Traceback (most recent call last):
280
ValueError: Not a good dotted-quad IP: 255.255.255.256
283
# import here to avoid it when ip_addr values are not used
284
import socket, struct
287
return struct.unpack('!L',
288
socket.inet_aton(ip.strip()))[0]
290
# bug in inet_aton, corrected in Python 2.3
291
if ip.strip() == '255.255.255.255':
294
raise ValueError('Not a good dotted-quad IP: %s' % ip)
297
def numToDottedQuad(num):
299
Convert long int to dotted quad string
301
>>> numToDottedQuad(-1L)
302
Traceback (most recent call last):
303
ValueError: Not a good numeric IP: -1
304
>>> numToDottedQuad(1L)
306
>>> numToDottedQuad(16777218L)
308
>>> numToDottedQuad(16908291L)
310
>>> numToDottedQuad(16909060L)
312
>>> numToDottedQuad(4294967295L)
314
>>> numToDottedQuad(4294967296L)
315
Traceback (most recent call last):
316
ValueError: Not a good numeric IP: 4294967296
319
# import here to avoid it when ip_addr values are not used
320
import socket, struct
322
# no need to intercept here, 4294967295L is fine
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)
329
class ValidateError(Exception):
331
This error indicates that the check failed.
332
It can be the base class for more specific errors.
334
Any check function that fails ought to raise this error.
337
>>> raise ValidateError
338
Traceback (most recent call last):
342
class VdtMissingValue(ValidateError):
343
"""No value was supplied to a check that needed one."""
345
class VdtUnknownCheckError(ValidateError):
346
"""An unknown check function was requested"""
348
def __init__(self, value):
350
>>> raise VdtUnknownCheckError('yoda')
351
Traceback (most recent call last):
352
VdtUnknownCheckError: the check "yoda" is unknown.
354
ValidateError.__init__(
356
'the check "%s" is unknown.' % value)
358
class VdtParamError(SyntaxError):
359
"""An incorrect parameter was passed"""
361
def __init__(self, name, value):
363
>>> raise VdtParamError('yoda', 'jedi')
364
Traceback (most recent call last):
365
VdtParamError: passed an incorrect value "jedi" for parameter "yoda".
367
SyntaxError.__init__(
369
'passed an incorrect value "%s" for parameter "%s".' % (
372
class VdtTypeError(ValidateError):
373
"""The value supplied was of the wrong type"""
375
def __init__(self, value):
377
>>> raise VdtTypeError('jedi')
378
Traceback (most recent call last):
379
VdtTypeError: the value "jedi" is of the wrong type.
381
ValidateError.__init__(
383
'the value "%s" is of the wrong type.' % value)
385
class VdtValueError(ValidateError):
387
The value supplied was of the correct type, but was not an allowed value.
390
def __init__(self, value):
392
>>> raise VdtValueError('jedi')
393
Traceback (most recent call last):
394
VdtValueError: the value "jedi" is unacceptable.
396
ValidateError.__init__(
398
'the value "%s" is unacceptable.' % value)
400
class VdtValueTooSmallError(VdtValueError):
401
"""The value supplied was of the correct type, but was too small."""
403
def __init__(self, value):
405
>>> raise VdtValueTooSmallError('0')
406
Traceback (most recent call last):
407
VdtValueTooSmallError: the value "0" is too small.
409
ValidateError.__init__(
411
'the value "%s" is too small.' % value)
413
class VdtValueTooBigError(VdtValueError):
414
"""The value supplied was of the correct type, but was too big."""
416
def __init__(self, value):
418
>>> raise VdtValueTooBigError('1')
419
Traceback (most recent call last):
420
VdtValueTooBigError: the value "1" is too big.
422
ValidateError.__init__(
424
'the value "%s" is too big.' % value)
426
class VdtValueTooShortError(VdtValueError):
427
"""The value supplied was of the correct type, but was too short."""
429
def __init__(self, value):
431
>>> raise VdtValueTooShortError('jed')
432
Traceback (most recent call last):
433
VdtValueTooShortError: the value "jed" is too short.
435
ValidateError.__init__(
437
'the value "%s" is too short.' % (value,))
439
class VdtValueTooLongError(VdtValueError):
440
"""The value supplied was of the correct type, but was too long."""
442
def __init__(self, value):
444
>>> raise VdtValueTooLongError('jedie')
445
Traceback (most recent call last):
446
VdtValueTooLongError: the value "jedie" is too long.
448
ValidateError.__init__(
450
'the value "%s" is too long.' % (value,))
452
class Validator(object):
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.
457
This can also involve converting the value from a string into
458
the correct datatype.
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.
463
An example input string would be:
464
'int_range(param1, param2)'
466
You would then provide something like:
468
>>> def int_range_check(value, min, max):
469
... # turn min and max from strings to integers
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
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)
491
>>> fdict = {'int_range': int_range_check}
492
>>> vtr1 = Validator(fdict)
493
>>> vtr1.check('int_range(20, 40)', '30')
495
>>> vtr1.check('int_range(20, 40)', '60')
496
Traceback (most recent call last):
497
VdtValueTooBigError: the value "60" is too big.
499
New functions can be added with : ::
501
>>> vtr2 = Validator()
502
>>> vtr2.functions['int_range'] = int_range_check
504
Or by passing in a dictionary of functions when Validator
507
Your functions *can* use keyword arguments,
508
but the first argument should always be 'value'.
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 : ::
514
keyword = function_name
515
keyword = function_name()
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
524
# this regex does the initial parsing of the checks
525
_func_re = re.compile(r'(.+?)\((.*)\)')
527
# this regex takes apart keyword arguments
528
_key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$')
531
# this regex finds keyword=list(....) type values
532
_list_arg = _list_arg
534
# this regex takes individual values out of lists - in one pass
535
_list_members = _list_members
537
# These regexes check a set of arguments for validity
538
# and then pull the members out
539
_paramfinder = re.compile(_paramstring, re.VERBOSE)
540
_matchfinder = re.compile(_matchstring, re.VERBOSE)
543
def __init__(self, functions=None):
545
>>> vtri = Validator()
549
'integer': is_integer,
552
'ip_addr': is_ip_addr,
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,
564
if functions is not None:
565
self.functions.update(functions)
566
# tekNico: for use by ConfigObj
567
self.baseErrorClass = ValidateError
569
def check(self, check, value, missing=False):
571
Usage: check(check, value)
574
check: string representing check to apply (including arguments)
575
value: object to be checked
576
Returns value, converted to correct type if necessary
578
If the check fails, raises a ``ValidateError`` subclass.
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.
587
fun_match = self._func_re.match(check)
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:
597
# pull out args of group 2
598
for arg in self._paramfinder.findall(arg_string):
599
# args may need whitespace removing (before removing quotes)
601
listmatch = self._list_arg.match(arg)
603
key, val = self._list_handle(listmatch)
604
fun_kwargs[key] = val
606
keymatch = self._key_arg.match(arg)
608
val = self._unquote(keymatch.group(2))
609
fun_kwargs[keymatch.group(1)] = val
612
fun_args.append(self._unquote(arg))
614
# allows for function names without (args)
615
(fun_name, fun_args, fun_kwargs) = (check, (), {})
619
value = fun_kwargs['default']
621
raise VdtMissingValue
626
# tekNico: default must be deleted if the value is specified too,
627
# otherwise the check function will get a spurious "default" keyword arg
629
del fun_kwargs['default']
633
fun = self.functions[fun_name]
635
raise VdtUnknownCheckError(fun_name)
639
return fun(value, *fun_args, **fun_kwargs)
641
def _unquote(self, val):
642
"""Unquote a value if necessary."""
643
if (len(val) > 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]):
647
def _list_handle(self, listmatch):
648
"""Take apart a ``keyword=list('val, 'val')`` type string."""
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))
656
def _pass(self, value):
658
Dummy check that always passes
660
>>> vtor.check('', 0)
662
>>> vtor.check('', '0')
668
def _is_num_param(names, values, to_float=False):
670
Return numbers from inputs or raise VdtParamError.
672
Lets ``None`` pass through.
673
Pass in keyword argument ``to_float=True`` to
674
use float for the conversion rather than int.
676
>>> _is_num_param(('', ''), (0, 1.0))
678
>>> _is_num_param(('', ''), (0, 1.0), to_float=True)
680
>>> _is_num_param(('a'), ('a'))
681
Traceback (most recent call last):
682
VdtParamError: passed an incorrect value "a" for parameter "a".
684
fun = to_float and float or int
686
for (name, val) in zip(names, values):
688
out_params.append(val)
689
elif isinstance(val, (int, long, float, StringTypes)):
691
out_params.append(fun(val))
692
except ValueError, e:
693
raise VdtParamError(name, val)
695
raise VdtParamError(name, val)
699
# you can override these by setting the appropriate name
700
# in Validator.functions
701
# note: if the params are specified wrongly in your input string,
702
# you will also raise errors.
704
def is_integer(value, min=None, max=None):
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
710
If the value is a string, then the conversion is done - if possible.
711
Otherwise a VdtError is raised.
713
>>> vtor.check('integer', '-1')
715
>>> vtor.check('integer', '0')
717
>>> vtor.check('integer', 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')
727
>>> vtor.check('integer(max=20)', '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)
744
# print value, type(value)
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
# if it's a string - does it represent an integer ?
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)
760
def is_float(value, min=None, max=None):
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.
765
If the value is a string, then the conversion is done - if possible.
766
Otherwise a VdtError is raised.
768
This can accept negative values.
770
>>> vtor.check('float', '2')
773
From now on we multiply the value to avoid comparing decimals
775
>>> vtor.check('float', '-6.8') * 10
777
>>> vtor.check('float', '12.2') * 10
779
>>> vtor.check('float', 8.4) * 10
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
786
>>> vtor.check('float(max=20.2)', '15.1') * 10
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.
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
# if it's a string - does it represent a float ?
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)
812
True: True, 'on': True, '1': True, 'true': True, 'yes': True,
813
False: False, 'off': False, '0': False, 'false': False, 'no': False,
818
Check if the value represents a boolean.
820
>>> vtor.check('boolean', 0)
822
>>> vtor.check('boolean', False)
824
>>> vtor.check('boolean', '0')
826
>>> vtor.check('boolean', 'off')
828
>>> vtor.check('boolean', 'false')
830
>>> vtor.check('boolean', 'no')
832
>>> vtor.check('boolean', 'nO')
834
>>> vtor.check('boolean', 'NO')
836
>>> vtor.check('boolean', 1)
838
>>> vtor.check('boolean', True)
840
>>> vtor.check('boolean', '1')
842
>>> vtor.check('boolean', 'on')
844
>>> vtor.check('boolean', 'true')
846
>>> vtor.check('boolean', 'yes')
848
>>> vtor.check('boolean', 'Yes')
850
>>> vtor.check('boolean', 'YES')
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.
860
if isinstance(value, StringTypes):
862
return bool_dict[value.lower()]
864
raise VdtTypeError(value)
865
# we do an equality test rather than an identity test
866
# this ensures Python 2.2 compatibilty
867
# and allows 0 and 1 to represent True and False
873
raise VdtTypeError(value)
876
def is_ip_addr(value):
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'.
881
>>> vtor.check('ip_addr', '1 ')
883
>>> vtor.check('ip_addr', ' 1.2')
885
>>> vtor.check('ip_addr', ' 1.2.3 ')
887
>>> vtor.check('ip_addr', '1.2.3.4')
889
>>> vtor.check('ip_addr', '0.0.0.0')
891
>>> vtor.check('ip_addr', '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.
906
if not isinstance(value, StringTypes):
907
raise VdtTypeError(value)
908
value = value.strip()
910
dottedQuadToNum(value)
912
raise VdtValueError(value)
915
def is_list(value, min=None, max=None):
917
Check that the value is a list of values.
919
You can optionally specify the minimum and maximum number of members.
921
It does no check on list members.
923
>>> vtor.check('list', ())
925
>>> vtor.check('list', [])
927
>>> vtor.check('list', (1, 2))
929
>>> vtor.check('list', [1, 2])
931
>>> vtor.check('list', '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))
941
>>> vtor.check('list', 0)
942
Traceback (most recent call last):
943
VdtTypeError: the value "0" is of the wrong type.
945
(min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
947
num_members = len(value)
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)
956
def is_string(value, min=None, max=None):
958
Check that the supplied value is a string.
960
You can optionally specify the minimum and maximum number of members.
962
>>> vtor.check('string', '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')
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')
974
>>> vtor.check('string(min=2, max=3)', '1234')
975
Traceback (most recent call last):
976
VdtValueTooLongError: the value "1234" is too long.
978
if not isinstance(value, StringTypes):
979
raise VdtTypeError(value)
980
return is_list(value, min, max)
982
def is_int_list(value, min=None, max=None):
984
Check that the value is a list of integers.
986
You can optionally specify the minimum and maximum number of members.
988
Each list member is checked that it is an integer.
990
>>> vtor.check('int_list', ())
992
>>> vtor.check('int_list', [])
994
>>> vtor.check('int_list', (1, 2))
996
>>> vtor.check('int_list', [1, 2])
998
>>> vtor.check('int_list', [1, 'a'])
999
Traceback (most recent call last):
1000
VdtTypeError: the value "a" is of the wrong type.
1002
return [is_integer(mem) for mem in is_list(value, min, max)]
1004
def is_bool_list(value, min=None, max=None):
1006
Check that the value is a list of booleans.
1008
You can optionally specify the minimum and maximum number of members.
1010
Each list member is checked that it is a boolean.
1012
>>> vtor.check('bool_list', ())
1014
>>> vtor.check('bool_list', [])
1016
>>> check_res = vtor.check('bool_list', (True, False))
1017
>>> check_res == [True, False]
1019
>>> check_res = vtor.check('bool_list', [True, False])
1020
>>> check_res == [True, False]
1022
>>> vtor.check('bool_list', [True, 'a'])
1023
Traceback (most recent call last):
1024
VdtTypeError: the value "a" is of the wrong type.
1026
return [is_bool(mem) for mem in is_list(value, min, max)]
1028
def is_float_list(value, min=None, max=None):
1030
Check that the value is a list of floats.
1032
You can optionally specify the minimum and maximum number of members.
1034
Each list member is checked that it is a float.
1036
>>> vtor.check('float_list', ())
1038
>>> vtor.check('float_list', [])
1040
>>> vtor.check('float_list', (1, 2.0))
1042
>>> vtor.check('float_list', [1, 2.0])
1044
>>> vtor.check('float_list', [1, 'a'])
1045
Traceback (most recent call last):
1046
VdtTypeError: the value "a" is of the wrong type.
1048
return [is_float(mem) for mem in is_list(value, min, max)]
1050
def is_string_list(value, min=None, max=None):
1052
Check that the value is a list of strings.
1054
You can optionally specify the minimum and maximum number of members.
1056
Each list member is checked that it is a string.
1058
>>> vtor.check('string_list', ())
1060
>>> vtor.check('string_list', [])
1062
>>> vtor.check('string_list', ('a', 'b'))
1064
>>> vtor.check('string_list', ['a', 1])
1065
Traceback (most recent call last):
1066
VdtTypeError: the value "1" is of the wrong type.
1068
return [is_string(mem) for mem in is_list(value, min, max)]
1070
def is_ip_addr_list(value, min=None, max=None):
1072
Check that the value is a list of IP addresses.
1074
You can optionally specify the minimum and maximum number of members.
1076
Each list member is checked that it is an IP address.
1078
>>> vtor.check('ip_addr_list', ())
1080
>>> vtor.check('ip_addr_list', [])
1082
>>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8'))
1083
['1.2.3.4', '5.6.7.8']
1084
>>> vtor.check('ip_addr_list', ['a'])
1085
Traceback (most recent call last):
1086
VdtValueError: the value "a" is unacceptable.
1088
return [is_ip_addr(mem) for mem in is_list(value, min, max)]
1091
'integer': is_integer,
1093
'ip_addr': is_ip_addr,
1094
'string': is_string,
1098
def is_mixed_list(value, *args):
1100
Check that the value is a list.
1101
Allow specifying the type of each member.
1102
Work on lists of specific lengths.
1104
You specify each member as a positional argument specifying type
1106
Each type should be one of the following strings :
1107
'integer', 'float', 'ip_addr', 'string', 'boolean'
1109
So you can specify a list of two strings, followed by
1112
mixed_list('string', 'string', 'integer', 'integer')
1114
The length of the list must match the number of positional
1115
arguments you supply.
1117
>>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')"
1118
>>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True))
1119
>>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
1121
>>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True'))
1122
>>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
1124
>>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True))
1125
Traceback (most recent call last):
1126
VdtTypeError: the value "b" is of the wrong type.
1127
>>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a'))
1128
Traceback (most recent call last):
1129
VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short.
1130
>>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b'))
1131
Traceback (most recent call last):
1132
VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long.
1133
>>> vtor.check(mix_str, 0)
1134
Traceback (most recent call last):
1135
VdtTypeError: the value "0" is of the wrong type.
1137
This test requires an elaborate setup, because of a change in error string
1138
output from the interpreter between Python 2.2 and 2.3 .
1141
... 'passed an incorrect value "',
1143
... '" for parameter "mixed_list".',
1145
>>> if INTP_VER == (2, 2):
1146
... res_str = "".join(res_seq)
1148
... res_str = "'".join(res_seq)
1150
... vtor.check('mixed_list("yoda")', ('a'))
1151
... except VdtParamError, err:
1152
... str(err) == res_str
1158
raise VdtTypeError(value)
1159
if length < len(args):
1160
raise VdtValueTooShortError(value)
1161
elif length > len(args):
1162
raise VdtValueTooLongError(value)
1164
return [fun_dict[arg](val) for arg, val in zip(args, value)]
1166
raise VdtParamError('mixed_list', e)
1168
def is_option(value, *options):
1170
This check matches the value to any of a set of options.
1172
>>> vtor.check('option("yoda", "jedi")', 'yoda')
1174
>>> vtor.check('option("yoda", "jedi")', 'jed')
1175
Traceback (most recent call last):
1176
VdtValueError: the value "jed" is unacceptable.
1177
>>> vtor.check('option("yoda", "jedi")', 0)
1178
Traceback (most recent call last):
1179
VdtTypeError: the value "0" is of the wrong type.
1181
if not isinstance(value, StringTypes):
1182
raise VdtTypeError(value)
1183
if not value in options:
1184
raise VdtValueError(value)
1187
def _test(value, *args, **keywargs):
1189
A function that exists for test purposes.
1192
... '3, 6, min=1, max=3, test=list(a, b, c)',
1196
... 'min=1, test="a b c"',
1197
... 'min=5, test="a, b, c"',
1198
... 'min=1, max=3, test="a, b, c"',
1199
... 'min=-100, test=-99',
1201
... '3, 6, test="36"',
1202
... '3, 6, test="a, b, c"',
1203
... '3, max=3, test=list("a", "b", "c")',
1204
... '''3, max=3, test=list("'a'", 'b', "x=(c)")''',
1205
... "test='x=fish(3)'",
1207
>>> v = Validator({'test': _test})
1208
>>> for entry in checks:
1209
... print v.check(('test(%s)' % entry), 3)
1210
(3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'})
1214
(3, (), {'test': 'a b c', 'min': '1'})
1215
(3, (), {'test': 'a, b, c', 'min': '5'})
1216
(3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'})
1217
(3, (), {'test': '-99', 'min': '-100'})
1218
(3, (), {'max': '3', 'min': '1'})
1219
(3, ('3', '6'), {'test': '36'})
1220
(3, ('3', '6'), {'test': 'a, b, c'})
1221
(3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'})
1222
(3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'})
1223
(3, (), {'test': 'x=fish(3)'})
1225
return (value, args, keywargs)
1228
if __name__ == '__main__':
1229
# run the code tests in doctest format
1231
m = sys.modules.get('__main__')
1232
globs = m.__dict__.copy()
1234
'INTP_VER': INTP_VER,
1235
'vtor': Validator(),
1237
doctest.testmod(m, globs=globs)
1243
Consider which parts of the regex stuff to put back in
1245
Can we implement a timestamp datatype ? (check DateUtil module)
1250
If we could pull tuples out of arguments, it would be easier
1251
to specify arguments for 'mixed_lists'.
1259
Fixed bug so we can handle keyword argument values with commas.
1261
We now use a list constructor for passing list values to keyword arguments
1262
(including ``default``) : ::
1264
default=list("val", "val", "val")
1266
Added the ``_test`` test. {sm;:-)}
1273
Moved a function call outside a try...except block.
1278
Most errors now prefixed ``Vdt``
1280
``VdtParamError`` no longer derives from ``VdtError``
1282
Finalised as version 0.2.0
1289
Removed the "length" argument for lists and strings, and related tests
1296
Deleted the "none" and "multiple" types and checks
1298
Added the None value for all types in Validation.check
1309
Fixed bug in Validator.check: when a value that has a default is also
1310
specified in the config file, the default must be deleted from fun_kwargs
1311
anyway, otherwise the check function will get a spurious "default" keyword
1314
Added "ip_addr_list" check
1321
Updated comments at top
1328
Added test for interpreter version: raises RuntimeError if earlier than
1331
Fixed last is_mixed_list test to work on Python 2.2 too
1338
Restored Python2.2 compatibility by avoiding usage of dict.pop
1345
Adjusted doctests for Python 2.2.3 compatibility, one test still fails
1346
for trivial reasons (string output delimiters)
1353
Added __version__, __all__, and __docformat__
1355
Replaced ``basestring`` with ``types.StringTypes``
1362
Reformatted final docstring in ReST format, indented it for easier folding
1369
Added an 'ip_addr' IPv4 address value check, with tests
1371
Updated the tests for mixed_list to include IP addresses
1373
Changed all references to value "tests" into value "checks", including
1374
the main Validator method, and all code tests
1381
Added even more code tests
1383
Refined the mixed_list check
1390
Introduced more VdtValueError subclasses
1392
Collapsed the ``_function_test`` and ``_function_parse`` methods into the
1395
Refined the value checks, using the new VdtValueError subclasses
1397
Changed "is_string" to use "is_list"
1399
Added many more code tests
1401
Changed the "bool" value type to "boolean"
1403
Some more code cleanup
1410
Code tests converted to doctest format and placed in the respective
1411
docstrings, so they are automatically checked, and easier to update
1413
Changed local vars "min" and "max" to "min_len", "max_len", "min_val" and
1414
"max_val", to avoid shadowing the builtin functions (but left function
1417
Uniformed value check function names to is_* convention
1419
``date`` type name changed to ``timestamp``
1421
Avoided some code duplication in list check functions
1423
Some more code cleanup
1428
Recoded the standard functions
1433
Improved paramfinder regex
1435
Ripped out all the regex stuff, checks, and the example functions