~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-06-10 07:11:27 UTC
  • Revision ID: mbp@sourcefrog.net-20050610071127-e65db6ee8166c286
- remove calls to bailout() from check code

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>