~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/util/configobj/validate.py

  • Committer: Robert Collins
  • Date: 2006-03-02 03:12:34 UTC
  • mto: (1594.2.4 integration)
  • mto: This revision was merged to the branch mainline in revision 1596.
  • Revision ID: robertc@robertcollins.net-20060302031234-cf6b75961f27c5df
InterVersionedFile implemented.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/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.
 
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
    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.1'
 
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_bool',
 
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)    # two groups
 
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)    # one group
 
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
# Python pre 2.2.1 doesn't have bool
 
251
try:
 
252
    bool
 
253
except NameError:
 
254
    def bool(val):
 
255
        """Simple boolean equivalent function. """
 
256
        if val:
 
257
            return 1
 
258
        else:
 
259
            return 0
 
260
 
 
261
def dottedQuadToNum(ip):
 
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
    # import here to avoid it when ip_addr values are not used
 
284
    import socket, struct
 
285
    
 
286
    try:
 
287
        return struct.unpack('!L',
 
288
            socket.inet_aton(ip.strip()))[0]
 
289
    except socket.error:
 
290
        # bug in inet_aton, corrected in Python 2.3
 
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
 
 
297
def numToDottedQuad(num):
 
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
    # import here to avoid it when ip_addr values are not used
 
320
    import socket, struct
 
321
    
 
322
    # no need to intercept here, 4294967295L is fine
 
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
 
 
329
class ValidateError(Exception):
 
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
 
 
342
class VdtMissingValue(ValidateError):
 
343
    """No value was supplied to a check that needed one."""
 
344
 
 
345
class VdtUnknownCheckError(ValidateError):
 
346
    """An unknown check function was requested"""
 
347
 
 
348
    def __init__(self, value):
 
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
 
 
358
class VdtParamError(SyntaxError):
 
359
    """An incorrect parameter was passed"""
 
360
 
 
361
    def __init__(self, name, value):
 
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
 
 
372
class VdtTypeError(ValidateError):
 
373
    """The value supplied was of the wrong type"""
 
374
 
 
375
    def __init__(self, value):
 
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
 
 
385
class VdtValueError(ValidateError):
 
386
    """
 
387
    The value supplied was of the correct type, but was not an allowed value.
 
388
    """
 
389
 
 
390
    def __init__(self, value):
 
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
 
 
400
class VdtValueTooSmallError(VdtValueError):
 
401
    """The value supplied was of the correct type, but was too small."""
 
402
 
 
403
    def __init__(self, value):
 
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
 
 
413
class VdtValueTooBigError(VdtValueError):
 
414
    """The value supplied was of the correct type, but was too big."""
 
415
 
 
416
    def __init__(self, value):
 
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
 
 
426
class VdtValueTooShortError(VdtValueError):
 
427
    """The value supplied was of the correct type, but was too short."""
 
428
 
 
429
    def __init__(self, value):
 
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
 
 
439
class VdtValueTooLongError(VdtValueError):
 
440
    """The value supplied was of the correct type, but was too long."""
 
441
 
 
442
    def __init__(self, value):
 
443
        """
 
444
        >>> raise VdtValueTooLongError('jedie')
 
445
        Traceback (most recent call last):
 
446
        VdtValueTooLongError: the value "jedie" is too long.
 
447
        """
 
448
        ValidateError.__init__(
 
449
            self,
 
450
            'the value "%s" is too long.' %  (value,))
 
451
 
 
452
class Validator(object):
 
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
    # this regex does the initial parsing of the checks
 
525
    _func_re = re.compile(r'(.+?)\((.*)\)')
 
526
 
 
527
    # this regex takes apart keyword arguments
 
528
    _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$')
 
529
 
 
530
 
 
531
    # this regex finds keyword=list(....) type values
 
532
    _list_arg = _list_arg
 
533
 
 
534
    # this regex takes individual values out of lists - in one pass
 
535
    _list_members = _list_members
 
536
 
 
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)
 
541
 
 
542
 
 
543
    def __init__(self, functions=None):
 
544
        """
 
545
        >>> vtri = Validator()
 
546
        """
 
547
        self.functions = {
 
548
            '': self._pass,
 
549
            'integer': is_integer,
 
550
            'float': is_float,
 
551
            'boolean': is_bool,
 
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
        # tekNico: for use by ConfigObj
 
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
                # Bad syntax
 
594
                raise VdtParamError
 
595
            fun_args = []
 
596
            fun_kwargs = {}
 
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)
 
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
            # allows for function names without (args)
 
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
# tekNico: default must be deleted if the value is specified too,
 
627
# otherwise the check function will get a spurious "default" keyword arg
 
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
##            print fun_args
 
638
##            print fun_kwargs
 
639
            return fun(value, *fun_args, **fun_kwargs)
 
640
 
 
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]):
 
644
            val = val[1:-1]
 
645
        return val
 
646
 
 
647
    def _list_handle(self, listmatch):
 
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
 
 
656
    def _pass(self, value):
 
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
 
 
668
def _is_num_param(names, values, to_float=False):
 
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
# built in checks
 
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.
 
703
 
 
704
def is_integer(value, min=None, max=None):
 
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
#    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 ?
 
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
        # if it's a string - does it represent a float ?
 
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
 
 
816
def is_bool(value):
 
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
    # 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
 
868
    if value == False:
 
869
        return False
 
870
    elif value == True:
 
871
        return True
 
872
    else:
 
873
        raise VdtTypeError(value)
 
874
 
 
875
 
 
876
def is_ip_addr(value):
 
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
 
 
956
def is_string(value, min=None, max=None):
 
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
    return is_list(value, min, max)
 
981
 
 
982
def is_int_list(value, min=None, max=None):
 
983
    """
 
984
    Check that the value is a list of integers.
 
985
    
 
986
    You can optionally specify the minimum and maximum number of members.
 
987
    
 
988
    Each list member is checked that it is an integer.
 
989
    
 
990
    >>> vtor.check('int_list', ())
 
991
    []
 
992
    >>> vtor.check('int_list', [])
 
993
    []
 
994
    >>> vtor.check('int_list', (1, 2))
 
995
    [1, 2]
 
996
    >>> vtor.check('int_list', [1, 2])
 
997
    [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.
 
1001
    """
 
1002
    return [is_integer(mem) for mem in is_list(value, min, max)]
 
1003
 
 
1004
def is_bool_list(value, min=None, max=None):
 
1005
    """
 
1006
    Check that the value is a list of booleans.
 
1007
    
 
1008
    You can optionally specify the minimum and maximum number of members.
 
1009
    
 
1010
    Each list member is checked that it is a boolean.
 
1011
    
 
1012
    >>> vtor.check('bool_list', ())
 
1013
    []
 
1014
    >>> vtor.check('bool_list', [])
 
1015
    []
 
1016
    >>> check_res = vtor.check('bool_list', (True, False))
 
1017
    >>> check_res == [True, False]
 
1018
    1
 
1019
    >>> check_res = vtor.check('bool_list', [True, False])
 
1020
    >>> check_res == [True, False]
 
1021
    1
 
1022
    >>> vtor.check('bool_list', [True, 'a'])
 
1023
    Traceback (most recent call last):
 
1024
    VdtTypeError: the value "a" is of the wrong type.
 
1025
    """
 
1026
    return [is_bool(mem) for mem in is_list(value, min, max)]
 
1027
 
 
1028
def is_float_list(value, min=None, max=None):
 
1029
    """
 
1030
    Check that the value is a list of floats.
 
1031
    
 
1032
    You can optionally specify the minimum and maximum number of members.
 
1033
    
 
1034
    Each list member is checked that it is a float.
 
1035
    
 
1036
    >>> vtor.check('float_list', ())
 
1037
    []
 
1038
    >>> vtor.check('float_list', [])
 
1039
    []
 
1040
    >>> vtor.check('float_list', (1, 2.0))
 
1041
    [1.0, 2.0]
 
1042
    >>> vtor.check('float_list', [1, 2.0])
 
1043
    [1.0, 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.
 
1047
    """
 
1048
    return [is_float(mem) for mem in is_list(value, min, max)]
 
1049
 
 
1050
def is_string_list(value, min=None, max=None):
 
1051
    """
 
1052
    Check that the value is a list of strings.
 
1053
    
 
1054
    You can optionally specify the minimum and maximum number of members.
 
1055
    
 
1056
    Each list member is checked that it is a string.
 
1057
    
 
1058
    >>> vtor.check('string_list', ())
 
1059
    []
 
1060
    >>> vtor.check('string_list', [])
 
1061
    []
 
1062
    >>> vtor.check('string_list', ('a', 'b'))
 
1063
    ['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.
 
1067
    """
 
1068
    return [is_string(mem) for mem in is_list(value, min, max)]
 
1069
 
 
1070
def is_ip_addr_list(value, min=None, max=None):
 
1071
    """
 
1072
    Check that the value is a list of IP addresses.
 
1073
    
 
1074
    You can optionally specify the minimum and maximum number of members.
 
1075
    
 
1076
    Each list member is checked that it is an IP address.
 
1077
    
 
1078
    >>> vtor.check('ip_addr_list', ())
 
1079
    []
 
1080
    >>> vtor.check('ip_addr_list', [])
 
1081
    []
 
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.
 
1087
    """
 
1088
    return [is_ip_addr(mem) for mem in is_list(value, min, max)]
 
1089
 
 
1090
fun_dict = {
 
1091
    'integer': is_integer,
 
1092
    'float': is_float,
 
1093
    'ip_addr': is_ip_addr,
 
1094
    'string': is_string,
 
1095
    'boolean': is_bool,
 
1096
}
 
1097
 
 
1098
def is_mixed_list(value, *args):
 
1099
    """
 
1100
    Check that the value is a list.
 
1101
    Allow specifying the type of each member.
 
1102
    Work on lists of specific lengths.
 
1103
    
 
1104
    You specify each member as a positional argument specifying type
 
1105
    
 
1106
    Each type should be one of the following strings :
 
1107
      'integer', 'float', 'ip_addr', 'string', 'boolean'
 
1108
    
 
1109
    So you can specify a list of two strings, followed by
 
1110
    two integers as :
 
1111
    
 
1112
      mixed_list('string', 'string', 'integer', 'integer')
 
1113
    
 
1114
    The length of the list must match the number of positional
 
1115
    arguments you supply.
 
1116
    
 
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]
 
1120
    1
 
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]
 
1123
    1
 
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.
 
1136
    
 
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 .
 
1139
    
 
1140
    >>> res_seq = (
 
1141
    ...     'passed an incorrect value "',
 
1142
    ...     'yoda',
 
1143
    ...     '" for parameter "mixed_list".',
 
1144
    ... )
 
1145
    >>> if INTP_VER == (2, 2):
 
1146
    ...     res_str = "".join(res_seq)
 
1147
    ... else:
 
1148
    ...     res_str = "'".join(res_seq)
 
1149
    >>> try:
 
1150
    ...     vtor.check('mixed_list("yoda")', ('a'))
 
1151
    ... except VdtParamError, err:
 
1152
    ...     str(err) == res_str
 
1153
    1
 
1154
    """
 
1155
    try:
 
1156
        length = len(value)
 
1157
    except TypeError:
 
1158
        raise VdtTypeError(value)
 
1159
    if length < len(args):
 
1160
        raise VdtValueTooShortError(value)
 
1161
    elif length > len(args):
 
1162
        raise VdtValueTooLongError(value)
 
1163
    try:
 
1164
        return [fun_dict[arg](val) for arg, val in zip(args, value)]
 
1165
    except KeyError, e:
 
1166
        raise VdtParamError('mixed_list', e)
 
1167
 
 
1168
def is_option(value, *options):
 
1169
    """
 
1170
    This check matches the value to any of a set of options.
 
1171
    
 
1172
    >>> vtor.check('option("yoda", "jedi")', 'yoda')
 
1173
    '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.
 
1180
    """
 
1181
    if not isinstance(value, StringTypes):
 
1182
        raise VdtTypeError(value)
 
1183
    if not value in options:
 
1184
        raise VdtValueError(value)
 
1185
    return value
 
1186
 
 
1187
def _test(value, *args, **keywargs):
 
1188
    """
 
1189
    A function that exists for test purposes.
 
1190
    
 
1191
    >>> checks = [
 
1192
    ...     '3, 6, min=1, max=3, test=list(a, b, c)',
 
1193
    ...     '3',
 
1194
    ...     '3, 6',
 
1195
    ...     '3,',
 
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',
 
1200
    ...     'min=1, max=3',
 
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)'",
 
1206
    ...    ]
 
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'})
 
1211
    (3, ('3',), {})
 
1212
    (3, ('3', '6'), {})
 
1213
    (3, ('3',), {})
 
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)'})
 
1224
    """
 
1225
    return (value, args, keywargs)
 
1226
 
 
1227
 
 
1228
if __name__ == '__main__':
 
1229
    # run the code tests in doctest format
 
1230
    import doctest
 
1231
    m = sys.modules.get('__main__')
 
1232
    globs = m.__dict__.copy()
 
1233
    globs.update({
 
1234
        'INTP_VER': INTP_VER,
 
1235
        'vtor': Validator(),
 
1236
    })
 
1237
    doctest.testmod(m, globs=globs)
 
1238
 
 
1239
"""
 
1240
    TODO
 
1241
    ====
 
1242
    
 
1243
    Consider which parts of the regex stuff to put back in
 
1244
    
 
1245
    Can we implement a timestamp datatype ? (check DateUtil module)
 
1246
    
 
1247
    ISSUES
 
1248
    ======
 
1249
    
 
1250
    If we could pull tuples out of arguments, it would be easier
 
1251
    to specify arguments for 'mixed_lists'.
 
1252
    
 
1253
    CHANGELOG
 
1254
    =========
 
1255
    
 
1256
    2005/12/16
 
1257
    ----------
 
1258
    
 
1259
    Fixed bug so we can handle keyword argument values with commas.
 
1260
    
 
1261
    We now use a list constructor for passing list values to keyword arguments
 
1262
    (including ``default``) : ::
 
1263
    
 
1264
        default=list("val", "val", "val")
 
1265
    
 
1266
    Added the ``_test`` test. {sm;:-)}
 
1267
    
 
1268
    0.2.1
 
1269
    
 
1270
    2005/12/12
 
1271
    ----------
 
1272
    
 
1273
    Moved a function call outside a try...except block.
 
1274
    
 
1275
    2005/08/25
 
1276
    ----------
 
1277
    
 
1278
    Most errors now prefixed ``Vdt``
 
1279
    
 
1280
    ``VdtParamError`` no longer derives from ``VdtError``
 
1281
    
 
1282
    Finalised as version 0.2.0
 
1283
    
 
1284
    2005/08/21
 
1285
    ----------
 
1286
    
 
1287
    By Nicola Larosa
 
1288
    
 
1289
    Removed the "length" argument for lists and strings, and related tests
 
1290
    
 
1291
    2005/08/16
 
1292
    ----------
 
1293
    
 
1294
    By Nicola Larosa
 
1295
    
 
1296
    Deleted the "none" and "multiple" types and checks
 
1297
    
 
1298
    Added the None value for all types in Validation.check
 
1299
    
 
1300
    2005/08/14
 
1301
    ----------
 
1302
    
 
1303
    By Michael Foord
 
1304
    
 
1305
    Removed timestamp.
 
1306
    
 
1307
    By Nicola Larosa
 
1308
    
 
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
 
1312
    argument
 
1313
    
 
1314
    Added "ip_addr_list" check
 
1315
    
 
1316
    2005/08/13
 
1317
    ----------
 
1318
    
 
1319
    By Nicola Larosa
 
1320
    
 
1321
    Updated comments at top
 
1322
    
 
1323
    2005/08/11
 
1324
    ----------
 
1325
    
 
1326
    By Nicola Larosa
 
1327
    
 
1328
    Added test for interpreter version: raises RuntimeError if earlier than
 
1329
    2.2
 
1330
    
 
1331
    Fixed last is_mixed_list test to work on Python 2.2 too
 
1332
    
 
1333
    2005/08/10
 
1334
    ----------
 
1335
    
 
1336
    By Nicola Larosa
 
1337
    
 
1338
    Restored Python2.2 compatibility by avoiding usage of dict.pop
 
1339
    
 
1340
    2005/08/07
 
1341
    ----------
 
1342
    
 
1343
    By Nicola Larosa
 
1344
    
 
1345
    Adjusted doctests for Python 2.2.3 compatibility, one test still fails
 
1346
    for trivial reasons (string output delimiters)
 
1347
    
 
1348
    2005/08/05
 
1349
    ----------
 
1350
    
 
1351
    By Michael Foord
 
1352
    
 
1353
    Added __version__, __all__, and __docformat__
 
1354
    
 
1355
    Replaced ``basestring`` with ``types.StringTypes``
 
1356
    
 
1357
    2005/07/28
 
1358
    ----------
 
1359
    
 
1360
    By Nicola Larosa
 
1361
    
 
1362
    Reformatted final docstring in ReST format, indented it for easier folding
 
1363
    
 
1364
    2005/07/20
 
1365
    ----------
 
1366
    
 
1367
    By Nicola Larosa
 
1368
    
 
1369
    Added an 'ip_addr' IPv4 address value check, with tests
 
1370
    
 
1371
    Updated the tests for mixed_list to include IP addresses
 
1372
    
 
1373
    Changed all references to value "tests" into value "checks", including
 
1374
    the main Validator method, and all code tests
 
1375
    
 
1376
    2005/07/19
 
1377
    ----------
 
1378
    
 
1379
    By Nicola Larosa
 
1380
    
 
1381
    Added even more code tests
 
1382
    
 
1383
    Refined the mixed_list check
 
1384
    
 
1385
    2005/07/18
 
1386
    ----------
 
1387
    
 
1388
    By Nicola Larosa
 
1389
    
 
1390
    Introduced more VdtValueError subclasses
 
1391
    
 
1392
    Collapsed the ``_function_test`` and ``_function_parse`` methods into the
 
1393
    ``check`` one
 
1394
    
 
1395
    Refined the value checks, using the new VdtValueError subclasses
 
1396
    
 
1397
    Changed "is_string" to use "is_list"
 
1398
    
 
1399
    Added many more code tests
 
1400
    
 
1401
    Changed the "bool" value type to "boolean"
 
1402
    
 
1403
    Some more code cleanup
 
1404
    
 
1405
    2005/07/17
 
1406
    ----------
 
1407
    
 
1408
    By Nicola Larosa
 
1409
    
 
1410
    Code tests converted to doctest format and placed in the respective
 
1411
    docstrings, so they are automatically checked, and easier to update
 
1412
    
 
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
 
1415
    parameters alone)
 
1416
    
 
1417
    Uniformed value check function names to is_* convention
 
1418
    
 
1419
    ``date`` type name changed to ``timestamp``
 
1420
    
 
1421
    Avoided some code duplication in list check functions
 
1422
    
 
1423
    Some more code cleanup
 
1424
    
 
1425
    2005/07/09
 
1426
    ----------
 
1427
    
 
1428
    Recoded the standard functions
 
1429
    
 
1430
    2005/07/08
 
1431
    ----------
 
1432
    
 
1433
    Improved paramfinder regex
 
1434
    
 
1435
    Ripped out all the regex stuff, checks, and the example functions
 
1436
    (to be replaced !)
 
1437
    
 
1438
    2005/07/06
 
1439
    ----------
 
1440
    
 
1441
    By Nicola Larosa
 
1442
    
 
1443
    Code cleanup
 
1444
"""
 
1445