~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/util/configobj/docs/validate.txt

  • Committer: Martin Pool
  • Date: 2006-01-13 06:31:42 UTC
  • Revision ID: mbp@sourcefrog.net-20060113063142-8e706dc1483c69e1
Bump version to 0.8pre

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
===================================
 
2
 Validation Schema with validate.py
 
3
===================================
 
4
 
 
5
--------------------------
 
6
 Using the Validator class
 
7
--------------------------
 
8
 
 
9
 
 
10
:Authors: `Michael Foord`_, `Nicola Larosa`_, `Mark Andrews`_
 
11
:Version: Validate 0.2.0
 
12
:Date: 2005/08/25
 
13
:Homepage: `Validate Homepage`_
 
14
:License: `BSD License`_
 
15
:Support: `Mailing List`_
 
16
 
 
17
.. _Mailing List: http://lists.sourceforge.net/lists/listinfo/configobj-develop
 
18
.. _Michael Foord: fuzzyman@voidspace.org.uk
 
19
.. _Nicola Larosa: nico@teknico.net
 
20
.. _Mark Andrews: mark@la-la.com
 
21
.. _This Document:
 
22
.. _Validate Homepage: http://www.voidspace.org.uk/python/validate.html
 
23
.. _BSD License: http://www.voidspace.org.uk/documents/BSD-LICENSE.txt
 
24
 
 
25
 
 
26
.. contents:: Validate Manual
 
27
 
 
28
Introduction
 
29
============
 
30
 
 
31
Validation is used to check that supplied values conform to a specification.
 
32
 
 
33
The value can be supplied as a string, e.g. from a config file. In this case
 
34
the check will also *convert* the value to the required type. This allows you
 
35
to add validation as a transparent layer to access data stored as strings. The
 
36
validation checks that the data is correct *and* converts it to the expected
 
37
type.
 
38
 
 
39
Checks are also strings, and are easy to write. One generic system can be used
 
40
to validate information from different sources, via a single consistent
 
41
mechanism.
 
42
 
 
43
Checks look like function calls, and map to function calls. They can include
 
44
parameters and keyword arguments. These arguments are passed to the relevant
 
45
function by the ``Validator`` instance, along with the value being checked.
 
46
 
 
47
The syntax for checks also allows for specifying a default value. This default
 
48
value can be ``None``, no matter what the type of the check. This can be used
 
49
to indicate that a value was missing, and so holds no useful value.
 
50
 
 
51
Functions either return a new value, or raise an exception. See `Validator
 
52
Exceptions`_ for the low down on the exception classes that ``validate.py``
 
53
defines.
 
54
 
 
55
Some standard functions are provided, for basic data types; these come built
 
56
into every validator. Additional checks are easy to write: they can be provided
 
57
when the ``Validator`` is instantiated, or added afterwards.
 
58
 
 
59
Validate was primarily written to support ConfigObj_, but was designed to be 
 
60
applicable to many other situations.
 
61
 
 
62
For support and bug reports please use the ConfigObj `Mailing List`_.
 
63
 
 
64
.. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html
 
65
 
 
66
Downloading
 
67
===========
 
68
 
 
69
The current version is **0.2.0**, dated 25th August 2005. 
 
70
 
 
71
You can get obtain validate in the following ways :
 
72
 
 
73
Files
 
74
-----
 
75
 
 
76
* validate.py_ from Voidspace
 
77
 
 
78
* configobj.zip from Voidspace - See the homepage of ConfigObj_ for the latest 
 
79
  version and downlaod links.
 
80
 
 
81
    This contains validate.py and `this document`_. (As well as ConfigObj_ and 
 
82
    the ConfigObj documentation).
 
83
    
 
84
* The latest development version can be obtained from the `Subversion Repository`_.
 
85
 
 
86
Documentation
 
87
-------------
 
88
 
 
89
*configobj.zip* contains `this document`_ and full `API Docs`_, generated 
 
90
automatically by EpyDoc_.
 
91
 
 
92
* You can view `this document`_ online as the `Validate Homepage`_.
 
93
 
 
94
* You can also browse the `API Docs`_ online.
 
95
 
 
96
Pythonutils
 
97
-----------
 
98
 
 
99
Validate_ is also part of the Pythonutils_ set of modules. This contains 
 
100
various other useful helper modules, and is required by many of the `Voidspace 
 
101
Python Projects`_.
 
102
 
 
103
.. _configobj.py: http://www.voidspace.org.uk/cgi-bin/voidspace/downman.py?file=configobj.py
 
104
.. _configobj.zip: http://www.voidspace.org.uk/cgi-bin/voidspace/downman.py?file=configobj-4.0.0b4.zip
 
105
.. _validate.py: http://www.voidspace.org.uk/cgi-bin/voidspace/downman.py?file=validate.py
 
106
.. _API Docs: http://www.voidspace.org.uk/python/configobj-api/
 
107
.. _Subversion Repository: http://svn.rest2web.python-hosting.com/branches/configobj4/
 
108
.. _Sourceforge: http://sourceforge.net/projects/configobj
 
109
.. _EpyDoc: http://epydoc.sourceforge.net
 
110
.. _pythonutils: http://www.voidspace.org.uk/python/pythonutils.html
 
111
.. _Voidspace Python Projects: http://www.voidspace.org.uk/python
 
112
.. _validate: http://www.voidspace.org.uk/python/validate.html
 
113
 
 
114
 
 
115
The standard functions
 
116
======================
 
117
 
 
118
The standard functions come built-in to every ``Validator`` instance. They work
 
119
with the following basic data types :
 
120
 
 
121
* integer
 
122
* float
 
123
* boolean
 
124
* string
 
125
* ip_addr
 
126
 
 
127
plus lists of these datatypes.
 
128
 
 
129
Adding additional checks is done through coding simple functions.
 
130
 
 
131
The full set of standard checks are :
 
132
 
 
133
:'integer': matches integer values (including negative). Takes optional 'min'
 
134
            and 'max' arguments : ::
 
135
 
 
136
                integer()
 
137
                integer(3, 9)    # any value from 3 to 9
 
138
                integer(min=0) # any positive value
 
139
                integer(max=9)
 
140
 
 
141
:'float': matches float values
 
142
          Has the same parameters as the integer check.
 
143
 
 
144
:'bool': matches boolean values: ``True`` or ``False``.
 
145
         Acceptable string values for True are : ::
 
146
 
 
147
             true, on, yes, 1
 
148
 
 
149
         Acceptable string values for False are : ::
 
150
 
 
151
             false, off, no, 0
 
152
 
 
153
         Any other value raises an error.
 
154
 
 
155
:'string': matches any string. Takes optional keyword args 'min' and 'max' to
 
156
           specify min and max length of string.
 
157
 
 
158
:'ip_addr': matches an Internet Protocol address, v.4, represented by a
 
159
            dotted-quad string, i.e. '1.2.3.4'.
 
160
 
 
161
:'list': matches any list. Takes optional keyword args 'min', and 'max' to
 
162
         specify min and max sizes of the list.
 
163
 
 
164
:'int_list': Matches a list of integers. Takes the same arguments as list.
 
165
 
 
166
:'float_list': Matches a list of floats. Takes the same arguments as list.
 
167
 
 
168
:'bool_list': Matches a list of boolean values. Takes the same arguments as
 
169
              list.
 
170
 
 
171
:'string_list': Matches a list of strings. Takes the same arguments as list.
 
172
 
 
173
:'ip_addr_list': Matches a list of IP addresses. Takes the same arguments as
 
174
                 list.
 
175
 
 
176
:'mixed_list': Matches a list with different types in specific positions.
 
177
               List size must match the number of arguments.
 
178
 
 
179
               Each position can be one of : ::
 
180
 
 
181
                   int, str, bool, float, ip_addr
 
182
 
 
183
               So to specify a list with two strings followed by two integers,
 
184
               you write the check as : ::
 
185
 
 
186
                   mixed_list(str, str, int, int)
 
187
 
 
188
:'pass': matches everything: it never fails and the value is unchanged. It is
 
189
         also the default if no check is specified.
 
190
 
 
191
:'option': matches any from a list of options.
 
192
           You specify this test with : ::
 
193
 
 
194
               option('option 1', 'option 2', 'option 3')
 
195
 
 
196
The following code will work without you having to specifically add the
 
197
functions yourself.
 
198
 
 
199
.. raw:: html
 
200
 
 
201
    {+coloring}
 
202
 
 
203
    from validate import Validator
 
204
    #
 
205
    vtor = Validator()
 
206
    newval1 = vtor.check(value1, 'integer')
 
207
    newval2 = vtor.check(value2, 'bool')
 
208
    # etc ...
 
209
 
 
210
    {-coloring}
 
211
 
 
212
.. note::
 
213
 
 
214
    Of course, if these checks fail they raise exceptions. So you should wrap
 
215
    them in ``try...except`` blocks. Better still,  use ConfigObj for a higher
 
216
    level interface.
 
217
 
 
218
Using Validator
 
219
===============
 
220
 
 
221
Using ``Validator`` is very easy. It has one public attribute and one public
 
222
method.
 
223
 
 
224
Shown below are the different steps in using ``Validator``.
 
225
 
 
226
The only additional thing you need to know, is about `Writing check
 
227
functions`_.
 
228
 
 
229
Instantiate
 
230
-----------
 
231
 
 
232
.. raw:: html
 
233
 
 
234
    {+coloring}
 
235
 
 
236
    from validate import Validator
 
237
    vtor = Validator()
 
238
 
 
239
    {-coloring}
 
240
 
 
241
or even :
 
242
 
 
243
.. raw:: html
 
244
 
 
245
    {+coloring}
 
246
 
 
247
    from validate import Validator
 
248
    #
 
249
    fdict = {
 
250
        'check_name1': function1,
 
251
        'check_name2': function2,
 
252
        'check_name3': function3,
 
253
    }
 
254
    #
 
255
    vtor = Validator(fdict)
 
256
 
 
257
    {-coloring}
 
258
 
 
259
The second method adds a set of your functions as soon as your validator is
 
260
created. They are stored in the ``vtor.functions`` dictionary. The 'key' you
 
261
give them in this dictionary is the name you use in your checks (not the
 
262
original function name).
 
263
 
 
264
Dictionary keys/functions you pass in can override the built-in ones if you
 
265
want.
 
266
 
 
267
Adding functions
 
268
----------------
 
269
 
 
270
The code shown above, for adding functions on instantiation, has exactly the
 
271
same effect as the following code :
 
272
 
 
273
.. raw:: html
 
274
 
 
275
    {+coloring}
 
276
 
 
277
    from validate import Validator
 
278
    #
 
279
    vtor = Validator()
 
280
    vtor.functions['check_name1'] = function1
 
281
    vtor.functions['check_name2'] = function2
 
282
    vtor.functions['check_name3'] = function3
 
283
 
 
284
    {-coloring}
 
285
 
 
286
``vtor.functions``is just a dictionary that maps names to functions, so we
 
287
could also have called ``vtor.functions.update(fdict)``.
 
288
 
 
289
Writing the check
 
290
-----------------
 
291
 
 
292
As we've heard, the checks map to the names in the ``functions`` dictionary.
 
293
You've got a full list of `The standard functions`_ and the arguments they
 
294
take.
 
295
 
 
296
If you're using ``Validator`` from ConfigObj, then your checks will look like
 
297
: ::
 
298
 
 
299
    keyword = int_list(max=6)
 
300
 
 
301
but the check part will be identical .
 
302
 
 
303
The check method
 
304
----------------
 
305
 
 
306
If you're not using ``Validator`` from ConfigObj, then you'll need to call the
 
307
``check`` method yourself.
 
308
 
 
309
If the check fails then it will raise an exception, so you'll want to trap
 
310
that. Here's the basic example :
 
311
 
 
312
.. raw:: html
 
313
 
 
314
    {+coloring}
 
315
 
 
316
    from validate import Validator, ValidateError
 
317
    #
 
318
    vtor = Validator()
 
319
    check = "integer(0, 9)"
 
320
    value = 3
 
321
    try:
 
322
        newvalue = vtor.check(check, value)
 
323
    except ValidateError:
 
324
        print 'Check Failed.'
 
325
    else:
 
326
        print 'Check passed.'
 
327
 
 
328
    {-coloring}
 
329
 
 
330
.. caution::
 
331
 
 
332
    Although the value can be a string, if it represents a list it should
 
333
    already have been turned into a list of strings.
 
334
 
 
335
Default Values
 
336
~~~~~~~~~~~~~~
 
337
 
 
338
Some values may not be available, and you may want to be able to specify a
 
339
default as part of the check.
 
340
 
 
341
You do this by passing the keyword ``missing=True`` to the ``check`` method, as
 
342
well as a ``default=value`` in the check. (Constructing these checks is done
 
343
automatically by ConfigObj: you only need to know about the ``default=value``
 
344
part) :
 
345
 
 
346
.. raw:: html
 
347
 
 
348
    {+coloring}
 
349
 
 
350
    check1 = 'integer(default=50)'
 
351
    check2 = 'option("val 1", "val 2", "val 3", default="val 1")'
 
352
 
 
353
    assert vtor.check('', check1, missing=True) == 50
 
354
    assert vtor.check('', check2, missing=True) == "val 1"
 
355
 
 
356
    {-coloring}
 
357
 
 
358
If you pass in ``missing=True`` to the check method, then the actual value is
 
359
ignored. If no default is specified in the check, a ``ValidateMissingValue``
 
360
exception is raised. If a default is specified then that is passed to the
 
361
check instead.
 
362
 
 
363
If the check has ``default=None`` (case sensitive) then ``vtor.check`` will
 
364
*always* return ``None`` (the object). This makes it easy to tell your program
 
365
that this check contains no useful value when missing, i.e. the value is
 
366
optional, and may be omitted without harm.
 
367
 
 
368
Validator Exceptions
 
369
====================
 
370
 
 
371
.. note::
 
372
 
 
373
    If you only use Validator through ConfigObj, it traps these Exceptions for
 
374
    you. You will still need to know about them for writing your own check
 
375
    functions.
 
376
 
 
377
``vtor.check`` indicates that the check has failed by raising an exception.
 
378
The appropriate error should be raised in the check function.
 
379
 
 
380
The base error class is ``ValidateError``. All errors (except for ``VdtParamError``) 
 
381
raised are sub-classes of this.
 
382
 
 
383
If an unrecognised check is specified then ``VdtUnknownCheckError`` is
 
384
raised.
 
385
 
 
386
There are also ``VdtTypeError`` and ``VdtValueError``.
 
387
 
 
388
If incorrect parameters are passed to a check function then it will (or should)
 
389
raise ``VdtParamError``. As this indicates *programmer* error, rather than an error
 
390
in the value, it is a subclass of ``SyntaxError`` instead of ``ValidateError``. 
 
391
 
 
392
.. note::
 
393
 
 
394
    This means it *won't* be caught by ConfigObj - but propagated instead.
 
395
 
 
396
If the value supplied is the wrong type, then the check should raise
 
397
``VdtTypeError``. e.g. the check requires the value to be an integer (or
 
398
representation of an integer) and something else was supplied.
 
399
 
 
400
If the value supplied is the right type, but an unacceptable value, then the
 
401
check should raise ``VdtValueError``. e.g. the check requires the value to
 
402
be an integer (or representation of an integer) less than ten and a higher
 
403
value was supplied.
 
404
 
 
405
Both ``VdtTypeError`` and ``VdtValueError`` are initialised with the
 
406
incorrect value. In other words you raise them like this :
 
407
 
 
408
.. raw:: html
 
409
 
 
410
    {+coloring}
 
411
 
 
412
    raise VdtTypeError(value)
 
413
    #
 
414
    raise VdtValueError(value)
 
415
 
 
416
    {-coloring}
 
417
 
 
418
``VdtValueError`` has the following subclasses, which should be raised if
 
419
they are more appropriate.
 
420
 
 
421
* ``VdtValueTooSmallError``
 
422
* ``VdtValueTooBigError``
 
423
* ``VdtValueTooShortError``
 
424
* ``VdtValueTooLongError``
 
425
 
 
426
Writing check functions
 
427
=======================
 
428
 
 
429
Writing check functions is easy.
 
430
 
 
431
The check function will receive the value as its first argument, followed by
 
432
any other parameters and keyword arguments.
 
433
 
 
434
If the check fails, it should raise a ``VdtTypeError`` or a
 
435
``VdtValueError`` (or an appropriate subclass).
 
436
 
 
437
All parameters and keyword arguments are *always* passed as strings. (Parsed
 
438
from the check string).
 
439
 
 
440
The value might be a string (or list of strings) and need
 
441
converting to the right type - alternatively it might already be a list of 
 
442
integers. Our function needs to be able to handle either.
 
443
 
 
444
If the check passes then it should return the value (possibly converted to the
 
445
right type).
 
446
 
 
447
And that's it !
 
448
 
 
449
Example
 
450
-------
 
451
 
 
452
Here is an example function that requires a list of integers. Each integer
 
453
must be between 0 and 99.
 
454
 
 
455
It takes a single argument specifying the length of the list. (Which allows us
 
456
to use the same check in more than one place). If the length can't be converted
 
457
to an integer then we need to raise ``VdtParamError``.
 
458
 
 
459
Next we check that the value is a list. Anything else should raise a
 
460
``VdtTypeError``. The list should also have 'length' entries. If the list
 
461
has more or less entries then we will need to raise a
 
462
``VdtValueTooShortError`` or a ``VdtValueTooLongError``.
 
463
 
 
464
Then we need to check every entry in the list. Each entry should be an integer
 
465
between 0 and 99, or a string representation of an integer between 0 and 99.
 
466
Any other type is a ``VdtTypeError``, any other value is a
 
467
``VdtValueError`` (either too big, or too small).
 
468
 
 
469
.. raw:: html
 
470
 
 
471
    {+coloring}
 
472
 
 
473
    def special_list(value, length):
 
474
        """
 
475
        Check that the supplied value is a list of integers,
 
476
        with 'length' entries, and each entry between 0 and 99.
 
477
        """
 
478
        # length is supplied as a string
 
479
        # we need to convert it to an integer
 
480
        try:
 
481
            length = int(length)
 
482
        except ValueError:
 
483
            raise VdtParamError('length', length)
 
484
        #
 
485
        # Check the supplied value is a list
 
486
        if not isinstance(value, list):
 
487
            raise VdtTypeError(value)
 
488
        #
 
489
        # check the length of the list is correct
 
490
        if len(value) > length:
 
491
            raise VdtValueTooLongError(value)
 
492
        elif len(value) < length:
 
493
            raise VdtValueTooShortError(value)
 
494
        #
 
495
        # Next, check every member in the list
 
496
        # converting strings as necessary
 
497
        out = []
 
498
        for entry in value:
 
499
            if not isinstance(entry, (str, unicode, int)):
 
500
                # a value in the list
 
501
                # is neither an integer nor a string
 
502
                raise VdtTypeError(value)
 
503
            elif isinstance(entry, (str, unicode)):
 
504
                if not entry.isdigit():
 
505
                    raise VdtTypeError(value)
 
506
                else:
 
507
                    entry = int(entry)
 
508
            if entry < 0:
 
509
                raise VdtValueTooSmallError(value)
 
510
            elif entry > 99:
 
511
                raise VdtValueTooBigError(value)
 
512
            out.append(entry)
 
513
        #
 
514
        # if we got this far, all is well
 
515
        # return the new list
 
516
        return out
 
517
 
 
518
    {-coloring}
 
519
 
 
520
If you are only using validate from ConfigObj then the error type (*TooBig*, 
 
521
*TooSmall*, etc) is lost - so you may only want to raise ``VdtValueError``.
 
522
 
 
523
.. caution::
 
524
 
 
525
    If your function raises an exception that isn't a subclass of 
 
526
    ``ValidateError``, then ConfigObj won't trap it. This means validation will 
 
527
    fail.
 
528
    
 
529
    This is why our function starts by checking the type of the value. If we 
 
530
    are passed the wrong type (e.g. an integer rather than a list) we get a 
 
531
    ``VdtTypeError`` rather than bombing out when we try to iterate over 
 
532
    the value.
 
533
 
 
534
If you are using validate in another circumstance you may want to create your 
 
535
own subclasses of ``ValidateError``, that convey more specific information.
 
536
 
 
537
TODO
 
538
====
 
539
 
 
540
* A regex check function ?
 
541
* A timestamp check function ? (Using the ``parse`` function from ``DateUtil``).
 
542
* Allow triple quotes ? (getting a bit heavy for a single regex)
 
543
 
 
544
ISSUES
 
545
======
 
546
 
 
547
.. note::
 
548
 
 
549
    Please file any bug reports to `Michael Foord`_ or the ConfigObj
 
550
    `Mailing List`_.
 
551
 
 
552
Lists passed as function arguments need additional quotes. Ugly, could do
 
553
with fixing this.
 
554
 
 
555
If we could pull tuples out of arguments, it would be easier
 
556
to specify arguments for 'mixed_lists'.
 
557
 
 
558
CHANGELOG
 
559
=========
 
560
 
 
561
 
 
562
2005/08/18      Version 0.2.0
 
563
-----------------------------
 
564
 
 
565
Updated by `Michael Foord`_ and `Nicola Larosa`_
 
566
 
 
567
Does type conversion as well.
 
568
 
 
569
2005/02/01      Version 0.1.0
 
570
-----------------------------
 
571
 
 
572
Initial version developed by `Michael Foord`_
 
573
and `Mark Andrews`_
 
574
 
 
575
.. note::
 
576
 
 
577
    Rendering this document with docutils also needs the
 
578
    textmacros module and the PySrc CSS stuff. See
 
579
    http://www.voidspace.org.uk/python/firedrop2/textmacros.shtml
 
580
 
 
581
.. raw:: html
 
582
 
 
583
    <div align="center">
 
584
        <a href="http://www.python.org">
 
585
            <img src="images/powered_by_python.jpg" width="602" height="186" border="0" />
 
586
        </a>
 
587
        <a href="http://www.opensource.org">
 
588
            <img src="images/osi-certified-120x100.gif" width="120" height="100" border="0" />
 
589
            <br /><strong>Certified Open Source</strong>
 
590
        </a>
 
591
        <br /><br />
 
592
        <script type="text/javascript" language="JavaScript">var site="s16atlantibots"</script>
 
593
        <script type="text/javascript" language="JavaScript1.2" src="http://s16.sitemeter.com/js/counter.js?site=s16atlantibots"></script>
 
594
        <noscript>
 
595
            <a href="http://s16.sitemeter.com/stats.asp?site=s16atlantibots">
 
596
                <img src="http://s16.sitemeter.com/meter.asp?site=s16atlantibots" alt="Site Meter" border=0 />
 
597
            </a>
 
598
        </noscript>
 
599
    </div>