~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Martin Pool
  • Date: 2006-06-10 23:16:19 UTC
  • mfrom: (1759 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1761.
  • Revision ID: mbp@sourcefrog.net-20060610231619-05b997deeb005d02
[merge] bzr.dev

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