~ubuntu-branches/debian/squeeze/configobj/squeeze

« back to all changes in this revision

Viewing changes to validate.py

  • Committer: Bazaar Package Importer
  • Author(s): Gustavo Noronha Silva
  • Date: 2006-05-07 22:49:58 UTC
  • Revision ID: james.westby@ubuntu.com-20060507224958-sbmw45csexigf431
Tags: upstream-4.3.1
ImportĀ upstreamĀ versionĀ 4.3.1

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.2'
 
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
    (min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
 
981
    try:
 
982
        num_members = len(value)
 
983
    except TypeError:
 
984
        raise VdtTypeError(value)
 
985
    if min_len is not None and num_members < min_len:
 
986
        raise VdtValueTooShortError(value)
 
987
    if max_len is not None and num_members > max_len:
 
988
        raise VdtValueTooLongError(value)
 
989
    return value
 
990
 
 
991
def is_int_list(value, min=None, max=None):
 
992
    """
 
993
    Check that the value is a list of integers.
 
994
    
 
995
    You can optionally specify the minimum and maximum number of members.
 
996
    
 
997
    Each list member is checked that it is an integer.
 
998
    
 
999
    >>> vtor.check('int_list', ())
 
1000
    []
 
1001
    >>> vtor.check('int_list', [])
 
1002
    []
 
1003
    >>> vtor.check('int_list', (1, 2))
 
1004
    [1, 2]
 
1005
    >>> vtor.check('int_list', [1, 2])
 
1006
    [1, 2]
 
1007
    >>> vtor.check('int_list', [1, 'a'])
 
1008
    Traceback (most recent call last):
 
1009
    VdtTypeError: the value "a" is of the wrong type.
 
1010
    """
 
1011
    return [is_integer(mem) for mem in is_list(value, min, max)]
 
1012
 
 
1013
def is_bool_list(value, min=None, max=None):
 
1014
    """
 
1015
    Check that the value is a list of booleans.
 
1016
    
 
1017
    You can optionally specify the minimum and maximum number of members.
 
1018
    
 
1019
    Each list member is checked that it is a boolean.
 
1020
    
 
1021
    >>> vtor.check('bool_list', ())
 
1022
    []
 
1023
    >>> vtor.check('bool_list', [])
 
1024
    []
 
1025
    >>> check_res = vtor.check('bool_list', (True, False))
 
1026
    >>> check_res == [True, False]
 
1027
    1
 
1028
    >>> check_res = vtor.check('bool_list', [True, False])
 
1029
    >>> check_res == [True, False]
 
1030
    1
 
1031
    >>> vtor.check('bool_list', [True, 'a'])
 
1032
    Traceback (most recent call last):
 
1033
    VdtTypeError: the value "a" is of the wrong type.
 
1034
    """
 
1035
    return [is_bool(mem) for mem in is_list(value, min, max)]
 
1036
 
 
1037
def is_float_list(value, min=None, max=None):
 
1038
    """
 
1039
    Check that the value is a list of floats.
 
1040
    
 
1041
    You can optionally specify the minimum and maximum number of members.
 
1042
    
 
1043
    Each list member is checked that it is a float.
 
1044
    
 
1045
    >>> vtor.check('float_list', ())
 
1046
    []
 
1047
    >>> vtor.check('float_list', [])
 
1048
    []
 
1049
    >>> vtor.check('float_list', (1, 2.0))
 
1050
    [1.0, 2.0]
 
1051
    >>> vtor.check('float_list', [1, 2.0])
 
1052
    [1.0, 2.0]
 
1053
    >>> vtor.check('float_list', [1, 'a'])
 
1054
    Traceback (most recent call last):
 
1055
    VdtTypeError: the value "a" is of the wrong type.
 
1056
    """
 
1057
    return [is_float(mem) for mem in is_list(value, min, max)]
 
1058
 
 
1059
def is_string_list(value, min=None, max=None):
 
1060
    """
 
1061
    Check that the value is a list of strings.
 
1062
    
 
1063
    You can optionally specify the minimum and maximum number of members.
 
1064
    
 
1065
    Each list member is checked that it is a string.
 
1066
    
 
1067
    >>> vtor.check('string_list', ())
 
1068
    []
 
1069
    >>> vtor.check('string_list', [])
 
1070
    []
 
1071
    >>> vtor.check('string_list', ('a', 'b'))
 
1072
    ['a', 'b']
 
1073
    >>> vtor.check('string_list', ['a', 1])
 
1074
    Traceback (most recent call last):
 
1075
    VdtTypeError: the value "1" is of the wrong type.
 
1076
    >>> vtor.check('string_list', 'hello')
 
1077
    Traceback (most recent call last):
 
1078
    VdtTypeError: the value "hello" is of the wrong type.
 
1079
    """
 
1080
    if isinstance(value, StringTypes):
 
1081
        raise VdtTypeError(value)
 
1082
    return [is_string(mem) for mem in is_list(value, min, max)]
 
1083
 
 
1084
def is_ip_addr_list(value, min=None, max=None):
 
1085
    """
 
1086
    Check that the value is a list of IP addresses.
 
1087
    
 
1088
    You can optionally specify the minimum and maximum number of members.
 
1089
    
 
1090
    Each list member is checked that it is an IP address.
 
1091
    
 
1092
    >>> vtor.check('ip_addr_list', ())
 
1093
    []
 
1094
    >>> vtor.check('ip_addr_list', [])
 
1095
    []
 
1096
    >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8'))
 
1097
    ['1.2.3.4', '5.6.7.8']
 
1098
    >>> vtor.check('ip_addr_list', ['a'])
 
1099
    Traceback (most recent call last):
 
1100
    VdtValueError: the value "a" is unacceptable.
 
1101
    """
 
1102
    return [is_ip_addr(mem) for mem in is_list(value, min, max)]
 
1103
 
 
1104
fun_dict = {
 
1105
    'integer': is_integer,
 
1106
    'float': is_float,
 
1107
    'ip_addr': is_ip_addr,
 
1108
    'string': is_string,
 
1109
    'boolean': is_bool,
 
1110
}
 
1111
 
 
1112
def is_mixed_list(value, *args):
 
1113
    """
 
1114
    Check that the value is a list.
 
1115
    Allow specifying the type of each member.
 
1116
    Work on lists of specific lengths.
 
1117
    
 
1118
    You specify each member as a positional argument specifying type
 
1119
    
 
1120
    Each type should be one of the following strings :
 
1121
      'integer', 'float', 'ip_addr', 'string', 'boolean'
 
1122
    
 
1123
    So you can specify a list of two strings, followed by
 
1124
    two integers as :
 
1125
    
 
1126
      mixed_list('string', 'string', 'integer', 'integer')
 
1127
    
 
1128
    The length of the list must match the number of positional
 
1129
    arguments you supply.
 
1130
    
 
1131
    >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')"
 
1132
    >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True))
 
1133
    >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
 
1134
    1
 
1135
    >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True'))
 
1136
    >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
 
1137
    1
 
1138
    >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True))
 
1139
    Traceback (most recent call last):
 
1140
    VdtTypeError: the value "b" is of the wrong type.
 
1141
    >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a'))
 
1142
    Traceback (most recent call last):
 
1143
    VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short.
 
1144
    >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b'))
 
1145
    Traceback (most recent call last):
 
1146
    VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long.
 
1147
    >>> vtor.check(mix_str, 0)
 
1148
    Traceback (most recent call last):
 
1149
    VdtTypeError: the value "0" is of the wrong type.
 
1150
    
 
1151
    This test requires an elaborate setup, because of a change in error string
 
1152
    output from the interpreter between Python 2.2 and 2.3 .
 
1153
    
 
1154
    >>> res_seq = (
 
1155
    ...     'passed an incorrect value "',
 
1156
    ...     'yoda',
 
1157
    ...     '" for parameter "mixed_list".',
 
1158
    ... )
 
1159
    >>> if INTP_VER == (2, 2):
 
1160
    ...     res_str = "".join(res_seq)
 
1161
    ... else:
 
1162
    ...     res_str = "'".join(res_seq)
 
1163
    >>> try:
 
1164
    ...     vtor.check('mixed_list("yoda")', ('a'))
 
1165
    ... except VdtParamError, err:
 
1166
    ...     str(err) == res_str
 
1167
    1
 
1168
    """
 
1169
    try:
 
1170
        length = len(value)
 
1171
    except TypeError:
 
1172
        raise VdtTypeError(value)
 
1173
    if length < len(args):
 
1174
        raise VdtValueTooShortError(value)
 
1175
    elif length > len(args):
 
1176
        raise VdtValueTooLongError(value)
 
1177
    try:
 
1178
        return [fun_dict[arg](val) for arg, val in zip(args, value)]
 
1179
    except KeyError, e:
 
1180
        raise VdtParamError('mixed_list', e)
 
1181
 
 
1182
def is_option(value, *options):
 
1183
    """
 
1184
    This check matches the value to any of a set of options.
 
1185
    
 
1186
    >>> vtor.check('option("yoda", "jedi")', 'yoda')
 
1187
    'yoda'
 
1188
    >>> vtor.check('option("yoda", "jedi")', 'jed')
 
1189
    Traceback (most recent call last):
 
1190
    VdtValueError: the value "jed" is unacceptable.
 
1191
    >>> vtor.check('option("yoda", "jedi")', 0)
 
1192
    Traceback (most recent call last):
 
1193
    VdtTypeError: the value "0" is of the wrong type.
 
1194
    """
 
1195
    if not isinstance(value, StringTypes):
 
1196
        raise VdtTypeError(value)
 
1197
    if not value in options:
 
1198
        raise VdtValueError(value)
 
1199
    return value
 
1200
 
 
1201
def _test(value, *args, **keywargs):
 
1202
    """
 
1203
    A function that exists for test purposes.
 
1204
    
 
1205
    >>> checks = [
 
1206
    ...     '3, 6, min=1, max=3, test=list(a, b, c)',
 
1207
    ...     '3',
 
1208
    ...     '3, 6',
 
1209
    ...     '3,',
 
1210
    ...     'min=1, test="a b c"',
 
1211
    ...     'min=5, test="a, b, c"',
 
1212
    ...     'min=1, max=3, test="a, b, c"',
 
1213
    ...     'min=-100, test=-99',
 
1214
    ...     'min=1, max=3',
 
1215
    ...     '3, 6, test="36"',
 
1216
    ...     '3, 6, test="a, b, c"',
 
1217
    ...     '3, max=3, test=list("a", "b", "c")',
 
1218
    ...     '''3, max=3, test=list("'a'", 'b', "x=(c)")''',
 
1219
    ...     "test='x=fish(3)'",
 
1220
    ...    ]
 
1221
    >>> v = Validator({'test': _test})
 
1222
    >>> for entry in checks:
 
1223
    ...     print v.check(('test(%s)' % entry), 3)
 
1224
    (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'})
 
1225
    (3, ('3',), {})
 
1226
    (3, ('3', '6'), {})
 
1227
    (3, ('3',), {})
 
1228
    (3, (), {'test': 'a b c', 'min': '1'})
 
1229
    (3, (), {'test': 'a, b, c', 'min': '5'})
 
1230
    (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'})
 
1231
    (3, (), {'test': '-99', 'min': '-100'})
 
1232
    (3, (), {'max': '3', 'min': '1'})
 
1233
    (3, ('3', '6'), {'test': '36'})
 
1234
    (3, ('3', '6'), {'test': 'a, b, c'})
 
1235
    (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'})
 
1236
    (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'})
 
1237
    (3, (), {'test': 'x=fish(3)'})
 
1238
    """
 
1239
    return (value, args, keywargs)
 
1240
 
 
1241
 
 
1242
if __name__ == '__main__':
 
1243
    # run the code tests in doctest format
 
1244
    import doctest
 
1245
    m = sys.modules.get('__main__')
 
1246
    globs = m.__dict__.copy()
 
1247
    globs.update({
 
1248
        'INTP_VER': INTP_VER,
 
1249
        'vtor': Validator(),
 
1250
    })
 
1251
    doctest.testmod(m, globs=globs)
 
1252
 
 
1253
"""
 
1254
    TODO
 
1255
    ====
 
1256
    
 
1257
    Consider which parts of the regex stuff to put back in
 
1258
    
 
1259
    Can we implement a timestamp datatype ? (check DateUtil module)
 
1260
    
 
1261
    ISSUES
 
1262
    ======
 
1263
    
 
1264
    If we could pull tuples out of arguments, it would be easier
 
1265
    to specify arguments for 'mixed_lists'.
 
1266
    
 
1267
    CHANGELOG
 
1268
    =========
 
1269
    
 
1270
    2006/04/23
 
1271
    ----------
 
1272
    
 
1273
    Addressed bug where a string would pass the ``is_list`` test. (Thanks to
 
1274
    Konrad Wojas.)
 
1275
    
 
1276
    2005/12/16
 
1277
    ----------
 
1278
    
 
1279
    Fixed bug so we can handle keyword argument values with commas.
 
1280
    
 
1281
    We now use a list constructor for passing list values to keyword arguments
 
1282
    (including ``default``) : ::
 
1283
    
 
1284
        default=list("val", "val", "val")
 
1285
    
 
1286
    Added the ``_test`` test. {sm;:-)}
 
1287
    
 
1288
    0.2.1
 
1289
    
 
1290
    2005/12/12
 
1291
    ----------
 
1292
    
 
1293
    Moved a function call outside a try...except block.
 
1294
    
 
1295
    2005/08/25
 
1296
    ----------
 
1297
    
 
1298
    Most errors now prefixed ``Vdt``
 
1299
    
 
1300
    ``VdtParamError`` no longer derives from ``VdtError``
 
1301
    
 
1302
    Finalised as version 0.2.0
 
1303
    
 
1304
    2005/08/21
 
1305
    ----------
 
1306
    
 
1307
    By Nicola Larosa
 
1308
    
 
1309
    Removed the "length" argument for lists and strings, and related tests
 
1310
    
 
1311
    2005/08/16
 
1312
    ----------
 
1313
    
 
1314
    By Nicola Larosa
 
1315
    
 
1316
    Deleted the "none" and "multiple" types and checks
 
1317
    
 
1318
    Added the None value for all types in Validation.check
 
1319
    
 
1320
    2005/08/14
 
1321
    ----------
 
1322
    
 
1323
    By Michael Foord
 
1324
    
 
1325
    Removed timestamp.
 
1326
    
 
1327
    By Nicola Larosa
 
1328
    
 
1329
    Fixed bug in Validator.check: when a value that has a default is also
 
1330
    specified in the config file, the default must be deleted from fun_kwargs
 
1331
    anyway, otherwise the check function will get a spurious "default" keyword
 
1332
    argument
 
1333
    
 
1334
    Added "ip_addr_list" check
 
1335
    
 
1336
    2005/08/13
 
1337
    ----------
 
1338
    
 
1339
    By Nicola Larosa
 
1340
    
 
1341
    Updated comments at top
 
1342
    
 
1343
    2005/08/11
 
1344
    ----------
 
1345
    
 
1346
    By Nicola Larosa
 
1347
    
 
1348
    Added test for interpreter version: raises RuntimeError if earlier than
 
1349
    2.2
 
1350
    
 
1351
    Fixed last is_mixed_list test to work on Python 2.2 too
 
1352
    
 
1353
    2005/08/10
 
1354
    ----------
 
1355
    
 
1356
    By Nicola Larosa
 
1357
    
 
1358
    Restored Python2.2 compatibility by avoiding usage of dict.pop
 
1359
    
 
1360
    2005/08/07
 
1361
    ----------
 
1362
    
 
1363
    By Nicola Larosa
 
1364
    
 
1365
    Adjusted doctests for Python 2.2.3 compatibility, one test still fails
 
1366
    for trivial reasons (string output delimiters)
 
1367
    
 
1368
    2005/08/05
 
1369
    ----------
 
1370
    
 
1371
    By Michael Foord
 
1372
    
 
1373
    Added __version__, __all__, and __docformat__
 
1374
    
 
1375
    Replaced ``basestring`` with ``types.StringTypes``
 
1376
    
 
1377
    2005/07/28
 
1378
    ----------
 
1379
    
 
1380
    By Nicola Larosa
 
1381
    
 
1382
    Reformatted final docstring in ReST format, indented it for easier folding
 
1383
    
 
1384
    2005/07/20
 
1385
    ----------
 
1386
    
 
1387
    By Nicola Larosa
 
1388
    
 
1389
    Added an 'ip_addr' IPv4 address value check, with tests
 
1390
    
 
1391
    Updated the tests for mixed_list to include IP addresses
 
1392
    
 
1393
    Changed all references to value "tests" into value "checks", including
 
1394
    the main Validator method, and all code tests
 
1395
    
 
1396
    2005/07/19
 
1397
    ----------
 
1398
    
 
1399
    By Nicola Larosa
 
1400
    
 
1401
    Added even more code tests
 
1402
    
 
1403
    Refined the mixed_list check
 
1404
    
 
1405
    2005/07/18
 
1406
    ----------
 
1407
    
 
1408
    By Nicola Larosa
 
1409
    
 
1410
    Introduced more VdtValueError subclasses
 
1411
    
 
1412
    Collapsed the ``_function_test`` and ``_function_parse`` methods into the
 
1413
    ``check`` one
 
1414
    
 
1415
    Refined the value checks, using the new VdtValueError subclasses
 
1416
    
 
1417
    Changed "is_string" to use "is_list"
 
1418
    
 
1419
    Added many more code tests
 
1420
    
 
1421
    Changed the "bool" value type to "boolean"
 
1422
    
 
1423
    Some more code cleanup
 
1424
    
 
1425
    2005/07/17
 
1426
    ----------
 
1427
    
 
1428
    By Nicola Larosa
 
1429
    
 
1430
    Code tests converted to doctest format and placed in the respective
 
1431
    docstrings, so they are automatically checked, and easier to update
 
1432
    
 
1433
    Changed local vars "min" and "max" to "min_len", "max_len", "min_val" and
 
1434
    "max_val", to avoid shadowing the builtin functions (but left function
 
1435
    parameters alone)
 
1436
    
 
1437
    Uniformed value check function names to is_* convention
 
1438
    
 
1439
    ``date`` type name changed to ``timestamp``
 
1440
    
 
1441
    Avoided some code duplication in list check functions
 
1442
    
 
1443
    Some more code cleanup
 
1444
    
 
1445
    2005/07/09
 
1446
    ----------
 
1447
    
 
1448
    Recoded the standard functions
 
1449
    
 
1450
    2005/07/08
 
1451
    ----------
 
1452
    
 
1453
    Improved paramfinder regex
 
1454
    
 
1455
    Ripped out all the regex stuff, checks, and the example functions
 
1456
    (to be replaced !)
 
1457
    
 
1458
    2005/07/06
 
1459
    ----------
 
1460
    
 
1461
    By Nicola Larosa
 
1462
    
 
1463
    Code cleanup
 
1464
"""
 
1465