~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Erik Bågfors
  • Date: 2006-02-03 19:50:59 UTC
  • mto: (1185.50.77 bzr-jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1554.
  • Revision ID: erik@bagfors.nu-20060203195059-1cf8ff5aa68de0ea
Support for plugins to register log formatters and set default formatter
Also, change one command line option for "log"

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