~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: 2005-04-26 05:51:17 UTC
  • Revision ID: mbp@sourcefrog.net-20050426055116-e4bede04e549e4f6
- update doc upload scripts

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>