~bzr-pqm/bzr/bzr.dev

1185.12.49 by Aaron Bentley
Switched to ConfigObj
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