~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-05-10 03:59:36 UTC
  • mto: This revision was merged to the branch mainline in revision 1707.
  • Revision ID: mbp@sourcefrog.net-20060510035936-1ca2a08c09240b31
Remove old empty notes directory

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.1
 
12
:Date: 2005/12/16
 
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/python/license.shtml
 
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 is 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.1**, dated 16th December 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 download 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.1.0.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('integer', value1)
 
207
    newval2 = vtor.check('bool', value2)
 
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
 
 
219
Using Validator
 
220
===============
 
221
 
 
222
Using ``Validator`` is very easy. It has one public attribute and one public
 
223
method.
 
224
 
 
225
Shown below are the different steps in using ``Validator``.
 
226
 
 
227
The only additional thing you need to know, is about `Writing check
 
228
functions`_.
 
229
 
 
230
Instantiate
 
231
-----------
 
232
 
 
233
.. raw:: html
 
234
 
 
235
    {+coloring}
 
236
 
 
237
    from validate import Validator
 
238
    vtor = Validator()
 
239
 
 
240
    {-coloring}
 
241
 
 
242
or even :
 
243
 
 
244
.. raw:: html
 
245
 
 
246
    {+coloring}
 
247
 
 
248
    from validate import Validator
 
249
    #
 
250
    fdict = {
 
251
        'check_name1': function1,
 
252
        'check_name2': function2,
 
253
        'check_name3': function3,
 
254
    }
 
255
    #
 
256
    vtor = Validator(fdict)
 
257
 
 
258
    {-coloring}
 
259
 
 
260
The second method adds a set of your functions as soon as your validator is
 
261
created. They are stored in the ``vtor.functions`` dictionary. The 'key' you
 
262
give them in this dictionary is the name you use in your checks (not the
 
263
original function name).
 
264
 
 
265
Dictionary keys/functions you pass in can override the built-in ones if you
 
266
want.
 
267
 
 
268
Adding functions
 
269
----------------
 
270
 
 
271
The code shown above, for adding functions on instantiation, has exactly the
 
272
same effect as the following code :
 
273
 
 
274
.. raw:: html
 
275
 
 
276
    {+coloring}
 
277
 
 
278
    from validate import Validator
 
279
    #
 
280
    vtor = Validator()
 
281
    vtor.functions['check_name1'] = function1
 
282
    vtor.functions['check_name2'] = function2
 
283
    vtor.functions['check_name3'] = function3
 
284
 
 
285
    {-coloring}
 
286
 
 
287
``vtor.functions``is just a dictionary that maps names to functions, so we
 
288
could also have called ``vtor.functions.update(fdict)``.
 
289
 
 
290
Writing the check
 
291
-----------------
 
292
 
 
293
As we've heard, the checks map to the names in the ``functions`` dictionary.
 
294
You've got a full list of `The standard functions`_ and the arguments they
 
295
take.
 
296
 
 
297
If you're using ``Validator`` from ConfigObj, then your checks will look like
 
298
: ::
 
299
 
 
300
    keyword = int_list(max=6)
 
301
 
 
302
but the check part will be identical .
 
303
 
 
304
The check method
 
305
----------------
 
306
 
 
307
If you're not using ``Validator`` from ConfigObj, then you'll need to call the
 
308
``check`` method yourself.
 
309
 
 
310
If the check fails then it will raise an exception, so you'll want to trap
 
311
that. Here's the basic example :
 
312
 
 
313
.. raw:: html
 
314
 
 
315
    {+coloring}
 
316
 
 
317
    from validate import Validator, ValidateError
 
318
    #
 
319
    vtor = Validator()
 
320
    check = "integer(0, 9)"
 
321
    value = 3
 
322
    try:
 
323
        newvalue = vtor.check(check, value)
 
324
    except ValidateError:
 
325
        print 'Check Failed.'
 
326
    else:
 
327
        print 'Check passed.'
 
328
 
 
329
    {-coloring}
 
330
 
 
331
.. caution::
 
332
 
 
333
    Although the value can be a string, if it represents a list it should
 
334
    already have been turned into a list of strings.
 
335
 
 
336
 
 
337
Default Values
 
338
~~~~~~~~~~~~~~
 
339
 
 
340
Some values may not be available, and you may want to be able to specify a
 
341
default as part of the check.
 
342
 
 
343
You do this by passing the keyword ``missing=True`` to the ``check`` method, as
 
344
well as a ``default=value`` in the check. (Constructing these checks is done
 
345
automatically by ConfigObj: you only need to know about the ``default=value``
 
346
part) :
 
347
 
 
348
.. raw:: html
 
349
 
 
350
    {+coloring}
 
351
 
 
352
    check1 = 'integer(default=50)'
 
353
    check2 = 'option("val 1", "val 2", "val 3", default="val 1")'
 
354
 
 
355
    assert vtor.check('', check1, missing=True) == 50
 
356
    assert vtor.check('', check2, missing=True) == "val 1"
 
357
 
 
358
    {-coloring}
 
359
 
 
360
If you pass in ``missing=True`` to the check method, then the actual value is
 
361
ignored. If no default is specified in the check, a ``ValidateMissingValue``
 
362
exception is raised. If a default is specified then that is passed to the
 
363
check instead.
 
364
 
 
365
If the check has ``default=None`` (case sensitive) then ``vtor.check`` will
 
366
*always* return ``None`` (the object). This makes it easy to tell your program
 
367
that this check contains no useful value when missing, i.e. the value is
 
368
optional, and may be omitted without harm.
 
369
 
 
370
 
 
371
List Values
 
372
~~~~~~~~~~~
 
373
 
 
374
It's possible that you would like your default value to be a list. It's even
 
375
possible that you will write your own check functions - and would like to pass
 
376
them keyword arguments as lists from within the check.
 
377
 
 
378
To avoid confusing syntax with commas and quotes you use a list constructor to
 
379
specify that keyword arguments are lists. This includes the ``default`` value.
 
380
This makes checks look something like : ::
 
381
 
 
382
    checkname(default=list('val1', 'val2', 'val3'))
 
383
 
 
384
Validator Exceptions
 
385
====================
 
386
 
 
387
.. note::
 
388
 
 
389
    If you only use Validator through ConfigObj, it traps these Exceptions for
 
390
    you. You will still need to know about them for writing your own check
 
391
    functions.
 
392
 
 
393
``vtor.check`` indicates that the check has failed by raising an exception.
 
394
The appropriate error should be raised in the check function.
 
395
 
 
396
The base error class is ``ValidateError``. All errors (except for ``VdtParamError``) 
 
397
raised are sub-classes of this.
 
398
 
 
399
If an unrecognised check is specified then ``VdtUnknownCheckError`` is
 
400
raised.
 
401
 
 
402
There are also ``VdtTypeError`` and ``VdtValueError``.
 
403
 
 
404
If incorrect parameters are passed to a check function then it will (or should)
 
405
raise ``VdtParamError``. As this indicates *programmer* error, rather than an error
 
406
in the value, it is a subclass of ``SyntaxError`` instead of ``ValidateError``. 
 
407
 
 
408
.. note::
 
409
 
 
410
    This means it *won't* be caught by ConfigObj - but propagated instead.
 
411
 
 
412
If the value supplied is the wrong type, then the check should raise
 
413
``VdtTypeError``. e.g. the check requires the value to be an integer (or
 
414
representation of an integer) and something else was supplied.
 
415
 
 
416
If the value supplied is the right type, but an unacceptable value, then the
 
417
check should raise ``VdtValueError``. e.g. the check requires the value to
 
418
be an integer (or representation of an integer) less than ten and a higher
 
419
value was supplied.
 
420
 
 
421
Both ``VdtTypeError`` and ``VdtValueError`` are initialised with the
 
422
incorrect value. In other words you raise them like this :
 
423
 
 
424
.. raw:: html
 
425
 
 
426
    {+coloring}
 
427
 
 
428
    raise VdtTypeError(value)
 
429
    #
 
430
    raise VdtValueError(value)
 
431
 
 
432
    {-coloring}
 
433
 
 
434
``VdtValueError`` has the following subclasses, which should be raised if
 
435
they are more appropriate.
 
436
 
 
437
* ``VdtValueTooSmallError``
 
438
* ``VdtValueTooBigError``
 
439
* ``VdtValueTooShortError``
 
440
* ``VdtValueTooLongError``
 
441
 
 
442
Writing check functions
 
443
=======================
 
444
 
 
445
Writing check functions is easy.
 
446
 
 
447
The check function will receive the value as its first argument, followed by
 
448
any other parameters and keyword arguments.
 
449
 
 
450
If the check fails, it should raise a ``VdtTypeError`` or a
 
451
``VdtValueError`` (or an appropriate subclass).
 
452
 
 
453
All parameters and keyword arguments are *always* passed as strings. (Parsed
 
454
from the check string).
 
455
 
 
456
The value might be a string (or list of strings) and need
 
457
converting to the right type - alternatively it might already be a list of 
 
458
integers. Our function needs to be able to handle either.
 
459
 
 
460
If the check passes then it should return the value (possibly converted to the
 
461
right type).
 
462
 
 
463
And that's it !
 
464
 
 
465
Example
 
466
-------
 
467
 
 
468
Here is an example function that requires a list of integers. Each integer
 
469
must be between 0 and 99.
 
470
 
 
471
It takes a single argument specifying the length of the list. (Which allows us
 
472
to use the same check in more than one place). If the length can't be converted
 
473
to an integer then we need to raise ``VdtParamError``.
 
474
 
 
475
Next we check that the value is a list. Anything else should raise a
 
476
``VdtTypeError``. The list should also have 'length' entries. If the list
 
477
has more or less entries then we will need to raise a
 
478
``VdtValueTooShortError`` or a ``VdtValueTooLongError``.
 
479
 
 
480
Then we need to check every entry in the list. Each entry should be an integer
 
481
between 0 and 99, or a string representation of an integer between 0 and 99.
 
482
Any other type is a ``VdtTypeError``, any other value is a
 
483
``VdtValueError`` (either too big, or too small).
 
484
 
 
485
.. raw:: html
 
486
 
 
487
    {+coloring}
 
488
 
 
489
    def special_list(value, length):
 
490
        """
 
491
        Check that the supplied value is a list of integers,
 
492
        with 'length' entries, and each entry between 0 and 99.
 
493
        """
 
494
        # length is supplied as a string
 
495
        # we need to convert it to an integer
 
496
        try:
 
497
            length = int(length)
 
498
        except ValueError:
 
499
            raise VdtParamError('length', length)
 
500
        #
 
501
        # Check the supplied value is a list
 
502
        if not isinstance(value, list):
 
503
            raise VdtTypeError(value)
 
504
        #
 
505
        # check the length of the list is correct
 
506
        if len(value) > length:
 
507
            raise VdtValueTooLongError(value)
 
508
        elif len(value) < length:
 
509
            raise VdtValueTooShortError(value)
 
510
        #
 
511
        # Next, check every member in the list
 
512
        # converting strings as necessary
 
513
        out = []
 
514
        for entry in value:
 
515
            if not isinstance(entry, (str, unicode, int)):
 
516
                # a value in the list
 
517
                # is neither an integer nor a string
 
518
                raise VdtTypeError(value)
 
519
            elif isinstance(entry, (str, unicode)):
 
520
                if not entry.isdigit():
 
521
                    raise VdtTypeError(value)
 
522
                else:
 
523
                    entry = int(entry)
 
524
            if entry < 0:
 
525
                raise VdtValueTooSmallError(value)
 
526
            elif entry > 99:
 
527
                raise VdtValueTooBigError(value)
 
528
            out.append(entry)
 
529
        #
 
530
        # if we got this far, all is well
 
531
        # return the new list
 
532
        return out
 
533
 
 
534
    {-coloring}
 
535
 
 
536
If you are only using validate from ConfigObj then the error type (*TooBig*, 
 
537
*TooSmall*, etc) is lost - so you may only want to raise ``VdtValueError``.
 
538
 
 
539
.. caution::
 
540
 
 
541
    If your function raises an exception that isn't a subclass of 
 
542
    ``ValidateError``, then ConfigObj won't trap it. This means validation will 
 
543
    fail.
 
544
    
 
545
    This is why our function starts by checking the type of the value. If we 
 
546
    are passed the wrong type (e.g. an integer rather than a list) we get a 
 
547
    ``VdtTypeError`` rather than bombing out when we try to iterate over 
 
548
    the value.
 
549
 
 
550
If you are using validate in another circumstance you may want to create your 
 
551
own subclasses of ``ValidateError``, that convey more specific information.
 
552
 
 
553
TODO
 
554
====
 
555
 
 
556
* A regex check function ?
 
557
* A timestamp check function ? (Using the ``parse`` function from ``DateUtil``).
 
558
* Allow triple quotes ? (getting a bit heavy for a regex)
 
559
 
 
560
ISSUES
 
561
======
 
562
 
 
563
.. note::
 
564
 
 
565
    Please file any bug reports to `Michael Foord`_ or the ConfigObj
 
566
    `Mailing List`_.
 
567
 
 
568
If we could pull tuples out of arguments, it would be easier
 
569
to specify arguments for 'mixed_lists'.
 
570
 
 
571
CHANGELOG
 
572
=========
 
573
 
 
574
2005/12/16      Version 0.2.1
 
575
-----------------------------
 
576
 
 
577
Fixed bug so we can handle keyword argument values with commas.
 
578
 
 
579
We now use a list constructor for passing list values to keyword arguments
 
580
(including ``default``) : ::
 
581
 
 
582
    default=list("val", "val", "val")
 
583
 
 
584
Added the ``_test`` test. {sm;:-)}
 
585
 
 
586
Moved a function call outside a try...except block.
 
587
 
 
588
2005/08/18      Version 0.2.0
 
589
-----------------------------
 
590
 
 
591
Updated by `Michael Foord`_ and `Nicola Larosa`_
 
592
 
 
593
Does type conversion as well.
 
594
 
 
595
2005/02/01      Version 0.1.0
 
596
-----------------------------
 
597
 
 
598
Initial version developed by `Michael Foord`_
 
599
and `Mark Andrews`_
 
600
 
 
601
.. note::
 
602
 
 
603
    Rendering this document with docutils also needs the
 
604
    textmacros module and the PySrc CSS stuff. See
 
605
    http://www.voidspace.org.uk/python/firedrop2/textmacros.shtml
 
606
 
 
607
.. raw:: html
 
608
 
 
609
    <div align="center">
 
610
        <a href="http://www.python.org">
 
611
            <img src="images/powered_by_python.jpg" width="602" height="186" border="0" />
 
612
        </a>
 
613
        <a href="http://www.opensource.org">
 
614
            <img src="images/osi-certified-120x100.gif" width="120" height="100" border="0" />
 
615
            <br /><strong>Certified Open Source</strong>
 
616
        </a>
 
617
        <br /><br />
 
618
        <script type="text/javascript" language="JavaScript">var site="s16atlantibots"</script>
 
619
        <script type="text/javascript" language="JavaScript1.2" src="http://s16.sitemeter.com/js/counter.js?site=s16atlantibots"></script>
 
620
        <noscript>
 
621
            <a href="http://s16.sitemeter.com/stats.asp?site=s16atlantibots">
 
622
                <img src="http://s16.sitemeter.com/meter.asp?site=s16atlantibots" alt="Site Meter" border=0 />
 
623
            </a>
 
624
        </noscript>
 
625
    </div>