~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Alexander Belchenko
  • Date: 2006-07-30 07:23:36 UTC
  • mto: (1711.2.111 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1906.
  • Revision ID: bialix@ukr.net-20060730072336-3e9fd7ddb67b5f47
More branding: bazaar-ng -> Bazaar; bazaar-ng.org -> bazaar-vcs.org

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/documents/BSD-LICENSE.txt
10
 
# Basically you're free to copy, modify, distribute and relicense it,
11
 
# So long as you keep a copy of the license with it.
12
 
 
13
 
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
14
 
# For information about bugfixes, updates and support, please join the
15
 
# ConfigObj mailing list:
16
 
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
17
 
# Comments, suggestions and bug reports welcome.
18
 
 
19
 
"""
20
 
    The Validator object is used to check that supplied values 
21
 
    conform to a specification.
22
 
    
23
 
    The value can be supplied as a string - e.g. from a config file.
24
 
    In this case the check will also *convert* the value to
25
 
    the required type. This allows you to add validation
26
 
    as a transparent layer to access data stored as strings.
27
 
    The validation checks that the data is correct *and*
28
 
    converts it to the expected type.
29
 
    
30
 
    Some standard checks are provided for basic data types.
31
 
    Additional checks are easy to write. They can be
32
 
    provided when the ``Validator`` is instantiated or
33
 
    added afterwards.
34
 
    
35
 
    The standard functions work with the following basic data types :
36
 
    
37
 
    * integers
38
 
    * floats
39
 
    * booleans
40
 
    * strings
41
 
    * ip_addr
42
 
    
43
 
    plus lists of these datatypes
44
 
    
45
 
    Adding additional checks is done through coding simple functions.
46
 
    
47
 
    The full set of standard checks are : 
48
 
    
49
 
    * 'integer': matches integer values (including negative)
50
 
                 Takes optional 'min' and 'max' arguments : ::
51
 
    
52
 
                   integer()
53
 
                   integer(3, 9)  # any value from 3 to 9
54
 
                   integer(min=0) # any positive value
55
 
                   integer(max=9)
56
 
    
57
 
    * 'float': matches float values
58
 
               Has the same parameters as the integer check.
59
 
    
60
 
    * 'boolean': matches boolean values - ``True`` or ``False``
61
 
                 Acceptable string values for True are :
62
 
                   true, on, yes, 1
63
 
                 Acceptable string values for False are :
64
 
                   false, off, no, 0
65
 
    
66
 
                 Any other value raises an error.
67
 
    
68
 
    * 'ip_addr': matches an Internet Protocol address, v.4, represented
69
 
                 by a dotted-quad string, i.e. '1.2.3.4'.
70
 
    
71
 
    * 'string': matches any string.
72
 
                Takes optional keyword args 'min' and 'max'
73
 
                to specify min and max lengths of the string.
74
 
    
75
 
    * 'list': matches any list.
76
 
              Takes optional keyword args 'min', and 'max' to specify min and
77
 
              max sizes of the list.
78
 
    
79
 
    * 'int_list': Matches a list of integers.
80
 
                  Takes the same arguments as list.
81
 
    
82
 
    * 'float_list': Matches a list of floats.
83
 
                    Takes the same arguments as list.
84
 
    
85
 
    * 'bool_list': Matches a list of boolean values.
86
 
                   Takes the same arguments as list.
87
 
    
88
 
    * 'ip_addr_list': Matches a list of IP addresses.
89
 
                     Takes the same arguments as list.
90
 
    
91
 
    * 'string_list': Matches a list of strings.
92
 
                     Takes the same arguments as list.
93
 
    
94
 
    * 'mixed_list': Matches a list with different types in 
95
 
                    specific positions. List size must match
96
 
                    the number of arguments.
97
 
    
98
 
                    Each position can be one of :
99
 
                    'integer', 'float', 'ip_addr', 'string', 'boolean'
100
 
    
101
 
                    So to specify a list with two strings followed
102
 
                    by two integers, you write the check as : ::
103
 
    
104
 
                      mixed_list('string', 'string', 'integer', 'integer')
105
 
    
106
 
    * 'pass': This check matches everything ! It never fails
107
 
              and the value is unchanged.
108
 
    
109
 
              It is also the default if no check is specified.
110
 
    
111
 
    * 'option': This check matches any from a list of options.
112
 
                You specify this check with : ::
113
 
    
114
 
                  option('option 1', 'option 2', 'option 3')
115
 
"""
116
 
 
117
 
__docformat__ = "restructuredtext en"
118
 
 
119
 
__version__ = '0.2.0'
120
 
 
121
 
__revision__ = '$Id: validate.py 123 2005-09-08 08:54:28Z fuzzyman $'
122
 
 
123
 
__all__ = (
124
 
    '__version__',
125
 
    'dottedQuadToNum',
126
 
    'numToDottedQuad',
127
 
    'ValidateError',
128
 
    'VdtUnknownCheckError',
129
 
    'VdtParamError',
130
 
    'VdtTypeError',
131
 
    'VdtValueError',
132
 
    'VdtValueTooSmallError',
133
 
    'VdtValueTooBigError',
134
 
    'VdtValueTooShortError',
135
 
    'VdtValueTooLongError',
136
 
    'VdtMissingValue',
137
 
    'Validator',
138
 
    'is_integer',
139
 
    'is_float',
140
 
    'is_bool',
141
 
    'is_list',
142
 
    'is_ip_addr',
143
 
    'is_string',
144
 
    'is_int_list',
145
 
    'is_bool_list',
146
 
    'is_float_list',
147
 
    'is_string_list',
148
 
    'is_ip_addr_list',
149
 
    'is_mixed_list',
150
 
    'is_option',
151
 
    '__docformat__',
152
 
)
153
 
 
154
 
import sys
155
 
INTP_VER = sys.version_info[:2]
156
 
if INTP_VER < (2, 2):
157
 
    raise RuntimeError("Python v.2.2 or later needed")
158
 
 
159
 
import re
160
 
StringTypes = (str, unicode)
161
 
 
162
 
# Python pre 2.2.1 doesn't have bool
163
 
try:
164
 
    bool
165
 
except NameError:
166
 
    def bool(val):
167
 
        """Simple boolean equivalent function. """
168
 
        if val:
169
 
            return 1
170
 
        else:
171
 
            return 0
172
 
 
173
 
def dottedQuadToNum(ip):
174
 
    """
175
 
    Convert decimal dotted quad string to long integer
176
 
    
177
 
    >>> dottedQuadToNum('1 ')
178
 
    1L
179
 
    >>> dottedQuadToNum(' 1.2')
180
 
    16777218L
181
 
    >>> dottedQuadToNum(' 1.2.3 ')
182
 
    16908291L
183
 
    >>> dottedQuadToNum('1.2.3.4')
184
 
    16909060L
185
 
    >>> dottedQuadToNum('1.2.3. 4')
186
 
    Traceback (most recent call last):
187
 
    ValueError: Not a good dotted-quad IP: 1.2.3. 4
188
 
    >>> dottedQuadToNum('255.255.255.255')
189
 
    4294967295L
190
 
    >>> dottedQuadToNum('255.255.255.256')
191
 
    Traceback (most recent call last):
192
 
    ValueError: Not a good dotted-quad IP: 255.255.255.256
193
 
    """
194
 
    
195
 
    # import here to avoid it when ip_addr values are not used
196
 
    import socket, struct
197
 
    
198
 
    try:
199
 
        return struct.unpack('!L',
200
 
            socket.inet_aton(ip.strip()))[0]
201
 
    except socket.error:
202
 
        # bug in inet_aton, corrected in Python 2.3
203
 
        if ip.strip() == '255.255.255.255':
204
 
            return 0xFFFFFFFFL
205
 
        else:
206
 
            raise ValueError('Not a good dotted-quad IP: %s' % ip)
207
 
    return
208
 
 
209
 
def numToDottedQuad(num):
210
 
    """
211
 
    Convert long int to dotted quad string
212
 
    
213
 
    >>> numToDottedQuad(-1L)
214
 
    Traceback (most recent call last):
215
 
    ValueError: Not a good numeric IP: -1
216
 
    >>> numToDottedQuad(1L)
217
 
    '0.0.0.1'
218
 
    >>> numToDottedQuad(16777218L)
219
 
    '1.0.0.2'
220
 
    >>> numToDottedQuad(16908291L)
221
 
    '1.2.0.3'
222
 
    >>> numToDottedQuad(16909060L)
223
 
    '1.2.3.4'
224
 
    >>> numToDottedQuad(4294967295L)
225
 
    '255.255.255.255'
226
 
    >>> numToDottedQuad(4294967296L)
227
 
    Traceback (most recent call last):
228
 
    ValueError: Not a good numeric IP: 4294967296
229
 
    """
230
 
    
231
 
    # import here to avoid it when ip_addr values are not used
232
 
    import socket, struct
233
 
    
234
 
    # no need to intercept here, 4294967295L is fine
235
 
    try:
236
 
        return socket.inet_ntoa(
237
 
            struct.pack('!L', long(num)))
238
 
    except (socket.error, struct.error, OverflowError):
239
 
        raise ValueError('Not a good numeric IP: %s' % num)
240
 
 
241
 
class ValidateError(Exception):
242
 
    """
243
 
    This error indicates that the check failed.
244
 
    It can be the base class for more specific errors.
245
 
    
246
 
    Any check function that fails ought to raise this error.
247
 
    (or a subclass)
248
 
    
249
 
    >>> raise ValidateError
250
 
    Traceback (most recent call last):
251
 
    ValidateError
252
 
    """
253
 
 
254
 
class VdtMissingValue(ValidateError):
255
 
    """No value was supplied to a check that needed one."""
256
 
 
257
 
class VdtUnknownCheckError(ValidateError):
258
 
    """An unknown check function was requested"""
259
 
 
260
 
    def __init__(self, value):
261
 
        """
262
 
        >>> raise VdtUnknownCheckError('yoda')
263
 
        Traceback (most recent call last):
264
 
        VdtUnknownCheckError: the check "yoda" is unknown.
265
 
        """
266
 
        ValidateError.__init__(
267
 
            self,
268
 
            'the check "%s" is unknown.' % value)
269
 
 
270
 
class VdtParamError(SyntaxError):
271
 
    """An incorrect parameter was passed"""
272
 
 
273
 
    def __init__(self, name, value):
274
 
        """
275
 
        >>> raise VdtParamError('yoda', 'jedi')
276
 
        Traceback (most recent call last):
277
 
        VdtParamError: passed an incorrect value "jedi" for parameter "yoda".
278
 
        """
279
 
        SyntaxError.__init__(
280
 
            self,
281
 
            'passed an incorrect value "%s" for parameter "%s".' % (
282
 
                value, name))
283
 
 
284
 
class VdtTypeError(ValidateError):
285
 
    """The value supplied was of the wrong type"""
286
 
 
287
 
    def __init__(self, value):
288
 
        """
289
 
        >>> raise VdtTypeError('jedi')
290
 
        Traceback (most recent call last):
291
 
        VdtTypeError: the value "jedi" is of the wrong type.
292
 
        """
293
 
        ValidateError.__init__(
294
 
            self,
295
 
            'the value "%s" is of the wrong type.' % value)
296
 
 
297
 
class VdtValueError(ValidateError):
298
 
    """
299
 
    The value supplied was of the correct type, but was not an allowed value.
300
 
    """
301
 
 
302
 
    def __init__(self, value):
303
 
        """
304
 
        >>> raise VdtValueError('jedi')
305
 
        Traceback (most recent call last):
306
 
        VdtValueError: the value "jedi" is unacceptable.
307
 
        """
308
 
        ValidateError.__init__(
309
 
            self,
310
 
            'the value "%s" is unacceptable.' % value)
311
 
 
312
 
class VdtValueTooSmallError(VdtValueError):
313
 
    """The value supplied was of the correct type, but was too small."""
314
 
 
315
 
    def __init__(self, value):
316
 
        """
317
 
        >>> raise VdtValueTooSmallError('0')
318
 
        Traceback (most recent call last):
319
 
        VdtValueTooSmallError: the value "0" is too small.
320
 
        """
321
 
        ValidateError.__init__(
322
 
            self,
323
 
            'the value "%s" is too small.' % value)
324
 
 
325
 
class VdtValueTooBigError(VdtValueError):
326
 
    """The value supplied was of the correct type, but was too big."""
327
 
 
328
 
    def __init__(self, value):
329
 
        """
330
 
        >>> raise VdtValueTooBigError('1')
331
 
        Traceback (most recent call last):
332
 
        VdtValueTooBigError: the value "1" is too big.
333
 
        """
334
 
        ValidateError.__init__(
335
 
            self,
336
 
            'the value "%s" is too big.' % value)
337
 
 
338
 
class VdtValueTooShortError(VdtValueError):
339
 
    """The value supplied was of the correct type, but was too short."""
340
 
 
341
 
    def __init__(self, value):
342
 
        """
343
 
        >>> raise VdtValueTooShortError('jed')
344
 
        Traceback (most recent call last):
345
 
        VdtValueTooShortError: the value "jed" is too short.
346
 
        """
347
 
        ValidateError.__init__(
348
 
            self,
349
 
            'the value "%s" is too short.' % (value,))
350
 
 
351
 
class VdtValueTooLongError(VdtValueError):
352
 
    """The value supplied was of the correct type, but was too long."""
353
 
 
354
 
    def __init__(self, value):
355
 
        """
356
 
        >>> raise VdtValueTooLongError('jedie')
357
 
        Traceback (most recent call last):
358
 
        VdtValueTooLongError: the value "jedie" is too long.
359
 
        """
360
 
        ValidateError.__init__(
361
 
            self,
362
 
            'the value "%s" is too long.' %  (value,))
363
 
 
364
 
class Validator(object):
365
 
    """
366
 
        Validator is an object that allows you to register a set of 'checks'.
367
 
        These checks take input and test that it conforms to the check.
368
 
        
369
 
        This can also involve converting the value from a string into
370
 
        the correct datatype.
371
 
        
372
 
        The ``check`` method takes an input string which configures which
373
 
        check is to be used and applies that check to a supplied value.
374
 
        
375
 
        An example input string would be:
376
 
        'int_range(param1, param2)'
377
 
        
378
 
        You would then provide something like:
379
 
        
380
 
        >>> def int_range_check(value, min, max):
381
 
        ...     # turn min and max from strings to integers
382
 
        ...     min = int(min)
383
 
        ...     max = int(max)
384
 
        ...     # check that value is of the correct type.
385
 
        ...     # possible valid inputs are integers or strings
386
 
        ...     # that represent integers
387
 
        ...     if not isinstance(value, (int, long, StringTypes)):
388
 
        ...         raise VdtTypeError(value)
389
 
        ...     elif isinstance(value, StringTypes):
390
 
        ...         # if we are given a string
391
 
        ...         # attempt to convert to an integer
392
 
        ...         try:
393
 
        ...             value = int(value)
394
 
        ...         except ValueError:
395
 
        ...             raise VdtValueError(value)
396
 
        ...     # check the value is between our constraints
397
 
        ...     if not min <= value:
398
 
        ...          raise VdtValueTooSmallError(value)
399
 
        ...     if not value <= max:
400
 
        ...          raise VdtValueTooBigError(value)
401
 
        ...     return value
402
 
        
403
 
        >>> fdict = {'int_range': int_range_check}
404
 
        >>> vtr1 = Validator(fdict)
405
 
        >>> vtr1.check('int_range(20, 40)', '30')
406
 
        30
407
 
        >>> vtr1.check('int_range(20, 40)', '60')
408
 
        Traceback (most recent call last):
409
 
        VdtValueTooBigError: the value "60" is too big.
410
 
        
411
 
        New functions can be added with : ::
412
 
        
413
 
        >>> vtr2 = Validator()       
414
 
        >>> vtr2.functions['int_range'] = int_range_check
415
 
        
416
 
        Or by passing in a dictionary of functions when Validator 
417
 
        is instantiated.
418
 
        
419
 
        Your functions *can* use keyword arguments,
420
 
        but the first argument should always be 'value'.
421
 
        
422
 
        If the function doesn't take additional arguments,
423
 
        the parentheses are optional in the check.
424
 
        It can be written with either of : ::
425
 
        
426
 
            keyword = function_name
427
 
            keyword = function_name()
428
 
        
429
 
        The first program to utilise Validator() was Michael Foord's
430
 
        ConfigObj, an alternative to ConfigParser which supports lists and
431
 
        can validate a config file using a config schema.
432
 
        For more details on using Validator with ConfigObj see:
433
 
        http://www.voidspace.org.uk/python/configobj.html
434
 
    """
435
 
 
436
 
    # this regex pulls values out of a comma separated line
437
 
    _paramfinder = re.compile(r'''(?:'.*?')|(?:".*?")|(?:[^'",\s][^,]*)''')
438
 
    # this regex is used for finding keyword arguments
439
 
    _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$')
440
 
    # this regex does the initial parsing of the checks
441
 
    _func_re = re.compile(r'(.+?)\((.*)\)')
442
 
 
443
 
    def __init__(self, functions=None):
444
 
        """
445
 
        >>> vtri = Validator()
446
 
        """
447
 
        self.functions = {
448
 
            '': self._pass,
449
 
            'integer': is_integer,
450
 
            'float': is_float,
451
 
            'boolean': is_bool,
452
 
            'ip_addr': is_ip_addr,
453
 
            'string': is_string,
454
 
            'list': is_list,
455
 
            'int_list': is_int_list,
456
 
            'float_list': is_float_list,
457
 
            'bool_list': is_bool_list,
458
 
            'ip_addr_list': is_ip_addr_list,
459
 
            'string_list': is_string_list,
460
 
            'mixed_list': is_mixed_list,
461
 
            'pass': self._pass,
462
 
            'option': is_option,
463
 
        }
464
 
        if functions is not None:
465
 
            self.functions.update(functions)
466
 
        # tekNico: for use by ConfigObj
467
 
        self.baseErrorClass = ValidateError
468
 
 
469
 
    def check(self, check, value, missing=False):
470
 
        """
471
 
        Usage: check(check, value)
472
 
        
473
 
        Arguments:
474
 
            check: string representing check to apply (including arguments)
475
 
            value: object to be checked
476
 
        Returns value, converted to correct type if necessary
477
 
        
478
 
        If the check fails, raises a ``ValidateError`` subclass.
479
 
        
480
 
        >>> vtor.check('yoda', '')
481
 
        Traceback (most recent call last):
482
 
        VdtUnknownCheckError: the check "yoda" is unknown.
483
 
        >>> vtor.check('yoda()', '')
484
 
        Traceback (most recent call last):
485
 
        VdtUnknownCheckError: the check "yoda" is unknown.
486
 
        """
487
 
        fun_match = self._func_re.match(check)
488
 
        if fun_match:
489
 
            fun_name = fun_match.group(1)
490
 
            fun_args = []
491
 
            fun_kwargs = {}
492
 
            # pull out args of group 2
493
 
            for arg in self._paramfinder.findall(fun_match.group(2)):
494
 
                # args may need whitespace removing (before removing quotes)
495
 
                arg = arg.strip()
496
 
                keymatch = self._key_arg.match(arg)
497
 
                if keymatch:
498
 
                    val = keymatch.group(2)
499
 
                    if (val[0] in ("'", '"')) and (val[0] == val[-1]):
500
 
                        val = val[1:-1]
501
 
                    fun_kwargs[keymatch.group(1)] = val
502
 
                    continue
503
 
                #
504
 
                if (arg[0] in ("'", '"')) and (arg[0] == arg[-1]):
505
 
                    arg = arg[1:-1]
506
 
                fun_args.append(arg)
507
 
        else:
508
 
            # allows for function names without (args)
509
 
            (fun_name, fun_args, fun_kwargs) = (check, (), {})
510
 
        #
511
 
        if missing:
512
 
            try:
513
 
                value = fun_kwargs['default']
514
 
            except KeyError:
515
 
                raise VdtMissingValue
516
 
            if value == 'None':
517
 
                value = None
518
 
        if value is None:
519
 
            return None
520
 
# tekNico: default must be deleted if the value is specified too,
521
 
# otherwise the check function will get a spurious "default" keyword arg
522
 
        try:
523
 
            del fun_kwargs['default']
524
 
        except KeyError:
525
 
            pass
526
 
        try:
527
 
            return self.functions[fun_name](value, *fun_args, **fun_kwargs)
528
 
        except KeyError:
529
 
            raise VdtUnknownCheckError(fun_name)
530
 
 
531
 
    def _pass(self, value):
532
 
        """
533
 
        Dummy check that always passes
534
 
        
535
 
        >>> vtor.check('', 0)
536
 
        0
537
 
        >>> vtor.check('', '0')
538
 
        '0'
539
 
        """
540
 
        return value
541
 
 
542
 
 
543
 
def _is_num_param(names, values, to_float=False):
544
 
    """
545
 
    Return numbers from inputs or raise VdtParamError.
546
 
    
547
 
    Lets ``None`` pass through.
548
 
    Pass in keyword argument ``to_float=True`` to
549
 
    use float for the conversion rather than int.
550
 
    
551
 
    >>> _is_num_param(('', ''), (0, 1.0))
552
 
    [0, 1]
553
 
    >>> _is_num_param(('', ''), (0, 1.0), to_float=True)
554
 
    [0.0, 1.0]
555
 
    >>> _is_num_param(('a'), ('a'))
556
 
    Traceback (most recent call last):
557
 
    VdtParamError: passed an incorrect value "a" for parameter "a".
558
 
    """
559
 
    fun = to_float and float or int
560
 
    out_params = []
561
 
    for (name, val) in zip(names, values):
562
 
        if val is None:
563
 
            out_params.append(val)
564
 
        elif isinstance(val, (int, long, float, StringTypes)):
565
 
            try:
566
 
                out_params.append(fun(val))
567
 
            except ValueError, e:
568
 
                raise VdtParamError(name, val)
569
 
        else:
570
 
            raise VdtParamError(name, val)
571
 
    return out_params
572
 
 
573
 
# built in checks
574
 
# you can override these by setting the appropriate name
575
 
# in Validator.functions
576
 
# note: if the params are specified wrongly in your input string,
577
 
#       you will also raise errors.
578
 
 
579
 
def is_integer(value, min=None, max=None):
580
 
    """
581
 
    A check that tests that a given value is an integer (int, or long)
582
 
    and optionally, between bounds. A negative value is accepted, while
583
 
    a float will fail.
584
 
    
585
 
    If the value is a string, then the conversion is done - if possible.
586
 
    Otherwise a VdtError is raised.
587
 
    
588
 
    >>> vtor.check('integer', '-1')
589
 
    -1
590
 
    >>> vtor.check('integer', '0')
591
 
    0
592
 
    >>> vtor.check('integer', 9)
593
 
    9
594
 
    >>> vtor.check('integer', 'a')
595
 
    Traceback (most recent call last):
596
 
    VdtTypeError: the value "a" is of the wrong type.
597
 
    >>> vtor.check('integer', '2.2')
598
 
    Traceback (most recent call last):
599
 
    VdtTypeError: the value "2.2" is of the wrong type.
600
 
    >>> vtor.check('integer(10)', '20')
601
 
    20
602
 
    >>> vtor.check('integer(max=20)', '15')
603
 
    15
604
 
    >>> vtor.check('integer(10)', '9')
605
 
    Traceback (most recent call last):
606
 
    VdtValueTooSmallError: the value "9" is too small.
607
 
    >>> vtor.check('integer(10)', 9)
608
 
    Traceback (most recent call last):
609
 
    VdtValueTooSmallError: the value "9" is too small.
610
 
    >>> vtor.check('integer(max=20)', '35')
611
 
    Traceback (most recent call last):
612
 
    VdtValueTooBigError: the value "35" is too big.
613
 
    >>> vtor.check('integer(max=20)', 35)
614
 
    Traceback (most recent call last):
615
 
    VdtValueTooBigError: the value "35" is too big.
616
 
    >>> vtor.check('integer(0, 9)', False)
617
 
    0
618
 
    """
619
 
#    print value, type(value)
620
 
    (min_val, max_val) = _is_num_param(('min', 'max'), (min, max))
621
 
    if not isinstance(value, (int, long, StringTypes)):
622
 
        raise VdtTypeError(value)
623
 
    if isinstance(value, StringTypes):
624
 
        # if it's a string - does it represent an integer ?
625
 
        try:
626
 
            value = int(value)
627
 
        except ValueError:
628
 
            raise VdtTypeError(value)
629
 
    if (min_val is not None) and (value < min_val):
630
 
        raise VdtValueTooSmallError(value)
631
 
    if (max_val is not None) and (value > max_val):
632
 
        raise VdtValueTooBigError(value)
633
 
    return value
634
 
 
635
 
def is_float(value, min=None, max=None):
636
 
    """
637
 
    A check that tests that a given value is a float
638
 
    (an integer will be accepted), and optionally - that it is between bounds.
639
 
    
640
 
    If the value is a string, then the conversion is done - if possible.
641
 
    Otherwise a VdtError is raised.
642
 
    
643
 
    This can accept negative values.
644
 
    
645
 
    >>> vtor.check('float', '2')
646
 
    2.0
647
 
    
648
 
    From now on we multiply the value to avoid comparing decimals
649
 
    
650
 
    >>> vtor.check('float', '-6.8') * 10
651
 
    -68.0
652
 
    >>> vtor.check('float', '12.2') * 10
653
 
    122.0
654
 
    >>> vtor.check('float', 8.4) * 10
655
 
    84.0
656
 
    >>> vtor.check('float', 'a')
657
 
    Traceback (most recent call last):
658
 
    VdtTypeError: the value "a" is of the wrong type.
659
 
    >>> vtor.check('float(10.1)', '10.2') * 10
660
 
    102.0
661
 
    >>> vtor.check('float(max=20.2)', '15.1') * 10
662
 
    151.0
663
 
    >>> vtor.check('float(10.0)', '9.0')
664
 
    Traceback (most recent call last):
665
 
    VdtValueTooSmallError: the value "9.0" is too small.
666
 
    >>> vtor.check('float(max=20.0)', '35.0')
667
 
    Traceback (most recent call last):
668
 
    VdtValueTooBigError: the value "35.0" is too big.
669
 
    """
670
 
    (min_val, max_val) = _is_num_param(
671
 
        ('min', 'max'), (min, max), to_float=True)
672
 
    if not isinstance(value, (int, long, float, StringTypes)):
673
 
        raise VdtTypeError(value)
674
 
    if not isinstance(value, float):
675
 
        # if it's a string - does it represent a float ?
676
 
        try:
677
 
            value = float(value)
678
 
        except ValueError:
679
 
            raise VdtTypeError(value)
680
 
    if (min_val is not None) and (value < min_val):
681
 
        raise VdtValueTooSmallError(value)
682
 
    if (max_val is not None) and (value > max_val):
683
 
        raise VdtValueTooBigError(value)
684
 
    return value
685
 
 
686
 
bool_dict = {
687
 
    True: True, 'on': True, '1': True, 'true': True, 'yes': True, 
688
 
    False: False, 'off': False, '0': False, 'false': False, 'no': False,
689
 
}
690
 
 
691
 
def is_bool(value):
692
 
    """
693
 
    Check if the value represents a boolean.
694
 
    
695
 
    >>> vtor.check('boolean', 0)
696
 
    0
697
 
    >>> vtor.check('boolean', False)
698
 
    0
699
 
    >>> vtor.check('boolean', '0')
700
 
    0
701
 
    >>> vtor.check('boolean', 'off')
702
 
    0
703
 
    >>> vtor.check('boolean', 'false')
704
 
    0
705
 
    >>> vtor.check('boolean', 'no')
706
 
    0
707
 
    >>> vtor.check('boolean', 'nO')
708
 
    0
709
 
    >>> vtor.check('boolean', 'NO')
710
 
    0
711
 
    >>> vtor.check('boolean', 1)
712
 
    1
713
 
    >>> vtor.check('boolean', True)
714
 
    1
715
 
    >>> vtor.check('boolean', '1')
716
 
    1
717
 
    >>> vtor.check('boolean', 'on')
718
 
    1
719
 
    >>> vtor.check('boolean', 'true')
720
 
    1
721
 
    >>> vtor.check('boolean', 'yes')
722
 
    1
723
 
    >>> vtor.check('boolean', 'Yes')
724
 
    1
725
 
    >>> vtor.check('boolean', 'YES')
726
 
    1
727
 
    >>> vtor.check('boolean', '')
728
 
    Traceback (most recent call last):
729
 
    VdtTypeError: the value "" is of the wrong type.
730
 
    >>> vtor.check('boolean', 'up')
731
 
    Traceback (most recent call last):
732
 
    VdtTypeError: the value "up" is of the wrong type.
733
 
    
734
 
    """
735
 
    if isinstance(value, StringTypes):
736
 
        try:
737
 
            return bool_dict[value.lower()]
738
 
        except KeyError:
739
 
            raise VdtTypeError(value)
740
 
    # we do an equality test rather than an identity test
741
 
    # this ensures Python 2.2 compatibilty
742
 
    # and allows 0 and 1 to represent True and False
743
 
    if value == False:
744
 
        return False
745
 
    elif value == True:
746
 
        return True
747
 
    else:
748
 
        raise VdtTypeError(value)
749
 
 
750
 
 
751
 
def is_ip_addr(value):
752
 
    """
753
 
    Check that the supplied value is an Internet Protocol address, v.4,
754
 
    represented by a dotted-quad string, i.e. '1.2.3.4'.
755
 
    
756
 
    >>> vtor.check('ip_addr', '1 ')
757
 
    '1'
758
 
    >>> vtor.check('ip_addr', ' 1.2')
759
 
    '1.2'
760
 
    >>> vtor.check('ip_addr', ' 1.2.3 ')
761
 
    '1.2.3'
762
 
    >>> vtor.check('ip_addr', '1.2.3.4')
763
 
    '1.2.3.4'
764
 
    >>> vtor.check('ip_addr', '0.0.0.0')
765
 
    '0.0.0.0'
766
 
    >>> vtor.check('ip_addr', '255.255.255.255')
767
 
    '255.255.255.255'
768
 
    >>> vtor.check('ip_addr', '255.255.255.256')
769
 
    Traceback (most recent call last):
770
 
    VdtValueError: the value "255.255.255.256" is unacceptable.
771
 
    >>> vtor.check('ip_addr', '1.2.3.4.5')
772
 
    Traceback (most recent call last):
773
 
    VdtValueError: the value "1.2.3.4.5" is unacceptable.
774
 
    >>> vtor.check('ip_addr', '1.2.3. 4')
775
 
    Traceback (most recent call last):
776
 
    VdtValueError: the value "1.2.3. 4" is unacceptable.
777
 
    >>> vtor.check('ip_addr', 0)
778
 
    Traceback (most recent call last):
779
 
    VdtTypeError: the value "0" is of the wrong type.
780
 
    """
781
 
    if not isinstance(value, StringTypes):
782
 
        raise VdtTypeError(value)
783
 
    value = value.strip()
784
 
    try:
785
 
        dottedQuadToNum(value)
786
 
    except ValueError:
787
 
        raise VdtValueError(value)
788
 
    return value
789
 
 
790
 
def is_list(value, min=None, max=None):
791
 
    """
792
 
    Check that the value is a list of values.
793
 
    
794
 
    You can optionally specify the minimum and maximum number of members.
795
 
    
796
 
    It does no check on list members.
797
 
    
798
 
    >>> vtor.check('list', ())
799
 
    ()
800
 
    >>> vtor.check('list', [])
801
 
    []
802
 
    >>> vtor.check('list', (1, 2))
803
 
    (1, 2)
804
 
    >>> vtor.check('list', [1, 2])
805
 
    [1, 2]
806
 
    >>> vtor.check('list', '12')
807
 
    '12'
808
 
    >>> vtor.check('list(3)', (1, 2))
809
 
    Traceback (most recent call last):
810
 
    VdtValueTooShortError: the value "(1, 2)" is too short.
811
 
    >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6))
812
 
    Traceback (most recent call last):
813
 
    VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long.
814
 
    >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4))
815
 
    (1, 2, 3, 4)
816
 
    >>> vtor.check('list', 0)
817
 
    Traceback (most recent call last):
818
 
    VdtTypeError: the value "0" is of the wrong type.
819
 
    """
820
 
    (min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
821
 
    try:
822
 
        num_members = len(value)
823
 
    except TypeError:
824
 
        raise VdtTypeError(value)
825
 
    if min_len is not None and num_members < min_len:
826
 
        raise VdtValueTooShortError(value)
827
 
    if max_len is not None and num_members > max_len:
828
 
        raise VdtValueTooLongError(value)
829
 
    return value
830
 
 
831
 
def is_string(value, min=None, max=None):
832
 
    """
833
 
    Check that the supplied value is a string.
834
 
    
835
 
    You can optionally specify the minimum and maximum number of members.
836
 
    
837
 
    >>> vtor.check('string', '0')
838
 
    '0'
839
 
    >>> vtor.check('string', 0)
840
 
    Traceback (most recent call last):
841
 
    VdtTypeError: the value "0" is of the wrong type.
842
 
    >>> vtor.check('string(2)', '12')
843
 
    '12'
844
 
    >>> vtor.check('string(2)', '1')
845
 
    Traceback (most recent call last):
846
 
    VdtValueTooShortError: the value "1" is too short.
847
 
    >>> vtor.check('string(min=2, max=3)', '123')
848
 
    '123'
849
 
    >>> vtor.check('string(min=2, max=3)', '1234')
850
 
    Traceback (most recent call last):
851
 
    VdtValueTooLongError: the value "1234" is too long.
852
 
    """
853
 
    if not isinstance(value, StringTypes):
854
 
        raise VdtTypeError(value)
855
 
    return is_list(value, min, max)
856
 
 
857
 
def is_int_list(value, min=None, max=None):
858
 
    """
859
 
    Check that the value is a list of integers.
860
 
    
861
 
    You can optionally specify the minimum and maximum number of members.
862
 
    
863
 
    Each list member is checked that it is an integer.
864
 
    
865
 
    >>> vtor.check('int_list', ())
866
 
    []
867
 
    >>> vtor.check('int_list', [])
868
 
    []
869
 
    >>> vtor.check('int_list', (1, 2))
870
 
    [1, 2]
871
 
    >>> vtor.check('int_list', [1, 2])
872
 
    [1, 2]
873
 
    >>> vtor.check('int_list', [1, 'a'])
874
 
    Traceback (most recent call last):
875
 
    VdtTypeError: the value "a" is of the wrong type.
876
 
    """
877
 
    return [is_integer(mem) for mem in is_list(value, min, max)]
878
 
 
879
 
def is_bool_list(value, min=None, max=None):
880
 
    """
881
 
    Check that the value is a list of booleans.
882
 
    
883
 
    You can optionally specify the minimum and maximum number of members.
884
 
    
885
 
    Each list member is checked that it is a boolean.
886
 
    
887
 
    >>> vtor.check('bool_list', ())
888
 
    []
889
 
    >>> vtor.check('bool_list', [])
890
 
    []
891
 
    >>> check_res = vtor.check('bool_list', (True, False))
892
 
    >>> check_res == [True, False]
893
 
    1
894
 
    >>> check_res = vtor.check('bool_list', [True, False])
895
 
    >>> check_res == [True, False]
896
 
    1
897
 
    >>> vtor.check('bool_list', [True, 'a'])
898
 
    Traceback (most recent call last):
899
 
    VdtTypeError: the value "a" is of the wrong type.
900
 
    """
901
 
    return [is_bool(mem) for mem in is_list(value, min, max)]
902
 
 
903
 
def is_float_list(value, min=None, max=None):
904
 
    """
905
 
    Check that the value is a list of floats.
906
 
    
907
 
    You can optionally specify the minimum and maximum number of members.
908
 
    
909
 
    Each list member is checked that it is a float.
910
 
    
911
 
    >>> vtor.check('float_list', ())
912
 
    []
913
 
    >>> vtor.check('float_list', [])
914
 
    []
915
 
    >>> vtor.check('float_list', (1, 2.0))
916
 
    [1.0, 2.0]
917
 
    >>> vtor.check('float_list', [1, 2.0])
918
 
    [1.0, 2.0]
919
 
    >>> vtor.check('float_list', [1, 'a'])
920
 
    Traceback (most recent call last):
921
 
    VdtTypeError: the value "a" is of the wrong type.
922
 
    """
923
 
    return [is_float(mem) for mem in is_list(value, min, max)]
924
 
 
925
 
def is_string_list(value, min=None, max=None):
926
 
    """
927
 
    Check that the value is a list of strings.
928
 
    
929
 
    You can optionally specify the minimum and maximum number of members.
930
 
    
931
 
    Each list member is checked that it is a string.
932
 
    
933
 
    >>> vtor.check('string_list', ())
934
 
    []
935
 
    >>> vtor.check('string_list', [])
936
 
    []
937
 
    >>> vtor.check('string_list', ('a', 'b'))
938
 
    ['a', 'b']
939
 
    >>> vtor.check('string_list', ['a', 1])
940
 
    Traceback (most recent call last):
941
 
    VdtTypeError: the value "1" is of the wrong type.
942
 
    """
943
 
    return [is_string(mem) for mem in is_list(value, min, max)]
944
 
 
945
 
def is_ip_addr_list(value, min=None, max=None):
946
 
    """
947
 
    Check that the value is a list of IP addresses.
948
 
    
949
 
    You can optionally specify the minimum and maximum number of members.
950
 
    
951
 
    Each list member is checked that it is an IP address.
952
 
    
953
 
    >>> vtor.check('ip_addr_list', ())
954
 
    []
955
 
    >>> vtor.check('ip_addr_list', [])
956
 
    []
957
 
    >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8'))
958
 
    ['1.2.3.4', '5.6.7.8']
959
 
    >>> vtor.check('ip_addr_list', ['a'])
960
 
    Traceback (most recent call last):
961
 
    VdtValueError: the value "a" is unacceptable.
962
 
    """
963
 
    return [is_ip_addr(mem) for mem in is_list(value, min, max)]
964
 
 
965
 
fun_dict = {
966
 
    'integer': is_integer,
967
 
    'float': is_float,
968
 
    'ip_addr': is_ip_addr,
969
 
    'string': is_string,
970
 
    'boolean': is_bool,
971
 
}
972
 
 
973
 
def is_mixed_list(value, *args):
974
 
    """
975
 
    Check that the value is a list.
976
 
    Allow specifying the type of each member.
977
 
    Work on lists of specific lengths.
978
 
    
979
 
    You specify each member as a positional argument specifying type
980
 
    
981
 
    Each type should be one of the following strings :
982
 
      'integer', 'float', 'ip_addr', 'string', 'boolean'
983
 
    
984
 
    So you can specify a list of two strings, followed by
985
 
    two integers as :
986
 
    
987
 
      mixed_list('string', 'string', 'integer', 'integer')
988
 
    
989
 
    The length of the list must match the number of positional
990
 
    arguments you supply.
991
 
    
992
 
    >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')"
993
 
    >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True))
994
 
    >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
995
 
    1
996
 
    >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True'))
997
 
    >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
998
 
    1
999
 
    >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True))
1000
 
    Traceback (most recent call last):
1001
 
    VdtTypeError: the value "b" is of the wrong type.
1002
 
    >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a'))
1003
 
    Traceback (most recent call last):
1004
 
    VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short.
1005
 
    >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b'))
1006
 
    Traceback (most recent call last):
1007
 
    VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long.
1008
 
    >>> vtor.check(mix_str, 0)
1009
 
    Traceback (most recent call last):
1010
 
    VdtTypeError: the value "0" is of the wrong type.
1011
 
    
1012
 
    This test requires an elaborate setup, because of a change in error string
1013
 
    output from the interpreter between Python 2.2 and 2.3 .
1014
 
    
1015
 
    >>> res_seq = (
1016
 
    ...     'passed an incorrect value "',
1017
 
    ...     'yoda',
1018
 
    ...     '" for parameter "mixed_list".',
1019
 
    ... )
1020
 
    >>> if INTP_VER == (2, 2):
1021
 
    ...     res_str = "".join(res_seq)
1022
 
    ... else:
1023
 
    ...     res_str = "'".join(res_seq)
1024
 
    >>> try:
1025
 
    ...     vtor.check('mixed_list("yoda")', ('a'))
1026
 
    ... except VdtParamError, err:
1027
 
    ...     str(err) == res_str
1028
 
    1
1029
 
    """
1030
 
    try:
1031
 
        length = len(value)
1032
 
    except TypeError:
1033
 
        raise VdtTypeError(value)
1034
 
    if length < len(args):
1035
 
        raise VdtValueTooShortError(value)
1036
 
    elif length > len(args):
1037
 
        raise VdtValueTooLongError(value)
1038
 
    try:
1039
 
        return [fun_dict[arg](val) for arg, val in zip(args, value)]
1040
 
    except KeyError, e:
1041
 
        raise VdtParamError('mixed_list', e)
1042
 
 
1043
 
def is_option(value, *options):
1044
 
    """
1045
 
    This check matches the value to any of a set of options.
1046
 
    
1047
 
    >>> vtor.check('option("yoda", "jedi")', 'yoda')
1048
 
    'yoda'
1049
 
    >>> vtor.check('option("yoda", "jedi")', 'jed')
1050
 
    Traceback (most recent call last):
1051
 
    VdtValueError: the value "jed" is unacceptable.
1052
 
    >>> vtor.check('option("yoda", "jedi")', 0)
1053
 
    Traceback (most recent call last):
1054
 
    VdtTypeError: the value "0" is of the wrong type.
1055
 
    """
1056
 
    if not isinstance(value, StringTypes):
1057
 
        raise VdtTypeError(value)
1058
 
    if not value in options:
1059
 
        raise VdtValueError(value)
1060
 
    return value
1061
 
 
1062
 
if __name__ == '__main__':
1063
 
    # run the code tests in doctest format
1064
 
    import doctest
1065
 
    m = sys.modules.get('__main__')
1066
 
    globs = m.__dict__.copy()
1067
 
    globs.update({
1068
 
        'INTP_VER': INTP_VER,
1069
 
        'vtor': Validator(),
1070
 
    })
1071
 
    doctest.testmod(m, globs=globs)
1072
 
 
1073
 
"""
1074
 
    TODO
1075
 
    ====
1076
 
    
1077
 
    Consider which parts of the regex stuff to put back in
1078
 
    
1079
 
    Can we implement a timestamp datatype ? (check DateUtil module)
1080
 
    
1081
 
    ISSUES
1082
 
    ======
1083
 
    
1084
 
    Lists passed as function arguments need additional quotes. Ugly, could do
1085
 
    with fixing this.
1086
 
    
1087
 
    If we could pull tuples out of arguments, it would be easier
1088
 
    to specify arguments for 'mixed_lists'.
1089
 
    
1090
 
    CHANGELOG
1091
 
    =========
1092
 
    
1093
 
    2005/08/25
1094
 
    ----------
1095
 
    
1096
 
    Most errors now prefixed ``Vdt``
1097
 
    
1098
 
    ``VdtParamError`` no longer derives from ``VdtError``
1099
 
    
1100
 
    Finalised as version 0.2.0
1101
 
    
1102
 
    2005/08/21
1103
 
    ----------
1104
 
    
1105
 
    By Nicola Larosa
1106
 
    
1107
 
    Removed the "length" argument for lists and strings, and related tests
1108
 
    
1109
 
    2005/08/16
1110
 
    ----------
1111
 
    
1112
 
    By Nicola Larosa
1113
 
    
1114
 
    Deleted the "none" and "multiple" types and checks
1115
 
    
1116
 
    Added the None value for all types in Validation.check
1117
 
    
1118
 
    2005/08/14
1119
 
    ----------
1120
 
    
1121
 
    By Michael Foord
1122
 
    
1123
 
    Removed timestamp.
1124
 
    
1125
 
    By Nicola Larosa
1126
 
    
1127
 
    Fixed bug in Validator.check: when a value that has a default is also
1128
 
    specified in the config file, the default must be deleted from fun_kwargs
1129
 
    anyway, otherwise the check function will get a spurious "default" keyword
1130
 
    argument
1131
 
    
1132
 
    Added "ip_addr_list" check
1133
 
    
1134
 
    2005/08/13
1135
 
    ----------
1136
 
    
1137
 
    By Nicola Larosa
1138
 
    
1139
 
    Updated comments at top
1140
 
    
1141
 
    2005/08/11
1142
 
    ----------
1143
 
    
1144
 
    By Nicola Larosa
1145
 
    
1146
 
    Added test for interpreter version: raises RuntimeError if earlier than
1147
 
    2.2
1148
 
    
1149
 
    Fixed last is_mixed_list test to work on Python 2.2 too
1150
 
    
1151
 
    2005/08/10
1152
 
    ----------
1153
 
    
1154
 
    By Nicola Larosa
1155
 
    
1156
 
    Restored Python2.2 compatibility by avoiding usage of dict.pop
1157
 
    
1158
 
    2005/08/07
1159
 
    ----------
1160
 
    
1161
 
    By Nicola Larosa
1162
 
    
1163
 
    Adjusted doctests for Python 2.2.3 compatibility, one test still fails
1164
 
    for trivial reasons (string output delimiters)
1165
 
    
1166
 
    2005/08/05
1167
 
    ----------
1168
 
    
1169
 
    By Michael Foord
1170
 
    
1171
 
    Added __version__, __all__, and __docformat__
1172
 
    
1173
 
    Replaced ``basestring`` with ``types.StringTypes``
1174
 
    
1175
 
    2005/07/28
1176
 
    ----------
1177
 
    
1178
 
    By Nicola Larosa
1179
 
    
1180
 
    Reformatted final docstring in ReST format, indented it for easier folding
1181
 
    
1182
 
    2005/07/20
1183
 
    ----------
1184
 
    
1185
 
    By Nicola Larosa
1186
 
    
1187
 
    Added an 'ip_addr' IPv4 address value check, with tests
1188
 
    
1189
 
    Updated the tests for mixed_list to include IP addresses
1190
 
    
1191
 
    Changed all references to value "tests" into value "checks", including
1192
 
    the main Validator method, and all code tests
1193
 
    
1194
 
    2005/07/19
1195
 
    ----------
1196
 
    
1197
 
    By Nicola Larosa
1198
 
    
1199
 
    Added even more code tests
1200
 
    
1201
 
    Refined the mixed_list check
1202
 
    
1203
 
    2005/07/18
1204
 
    ----------
1205
 
    
1206
 
    By Nicola Larosa
1207
 
    
1208
 
    Introduced more VdtValueError subclasses
1209
 
    
1210
 
    Collapsed the ``_function_test`` and ``_function_parse`` methods into the
1211
 
    ``check`` one
1212
 
    
1213
 
    Refined the value checks, using the new VdtValueError subclasses
1214
 
    
1215
 
    Changed "is_string" to use "is_list"
1216
 
    
1217
 
    Added many more code tests
1218
 
    
1219
 
    Changed the "bool" value type to "boolean"
1220
 
    
1221
 
    Some more code cleanup
1222
 
    
1223
 
    2005/07/17
1224
 
    ----------
1225
 
    
1226
 
    By Nicola Larosa
1227
 
    
1228
 
    Code tests converted to doctest format and placed in the respective
1229
 
    docstrings, so they are automatically checked, and easier to update
1230
 
    
1231
 
    Changed local vars "min" and "max" to "min_len", "max_len", "min_val" and
1232
 
    "max_val", to avoid shadowing the builtin functions (but left function
1233
 
    parameters alone)
1234
 
    
1235
 
    Uniformed value check function names to is_* convention
1236
 
    
1237
 
    ``date`` type name changed to ``timestamp``
1238
 
    
1239
 
    Avoided some code duplication in list check functions
1240
 
    
1241
 
    Some more code cleanup
1242
 
    
1243
 
    2005/07/09
1244
 
    ----------
1245
 
    
1246
 
    Recoded the standard functions
1247
 
    
1248
 
    2005/07/08
1249
 
    ----------
1250
 
    
1251
 
    Improved paramfinder regex
1252
 
    
1253
 
    Ripped out all the regex stuff, checks, and the example functions
1254
 
    (to be replaced !)
1255
 
    
1256
 
    2005/07/06
1257
 
    ----------
1258
 
    
1259
 
    By Nicola Larosa
1260
 
    
1261
 
    Code cleanup
1262
 
"""
1263