|
1 # validate.py |
|
2 # A Validator object |
|
3 # Copyright (C) 2005-2010 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/python/license.shtml |
|
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. (Always returns a list.) |
|
78 |
|
79 * 'tuple': matches any tuple. |
|
80 Takes optional keyword args 'min', and 'max' to specify min and |
|
81 max sizes of the tuple. (Always returns a tuple.) |
|
82 |
|
83 * 'int_list': Matches a list of integers. |
|
84 Takes the same arguments as list. |
|
85 |
|
86 * 'float_list': Matches a list of floats. |
|
87 Takes the same arguments as list. |
|
88 |
|
89 * 'bool_list': Matches a list of boolean values. |
|
90 Takes the same arguments as list. |
|
91 |
|
92 * 'ip_addr_list': Matches a list of IP addresses. |
|
93 Takes the same arguments as list. |
|
94 |
|
95 * 'string_list': Matches a list of strings. |
|
96 Takes the same arguments as list. |
|
97 |
|
98 * 'mixed_list': Matches a list with different types in |
|
99 specific positions. List size must match |
|
100 the number of arguments. |
|
101 |
|
102 Each position can be one of : |
|
103 'integer', 'float', 'ip_addr', 'string', 'boolean' |
|
104 |
|
105 So to specify a list with two strings followed |
|
106 by two integers, you write the check as : :: |
|
107 |
|
108 mixed_list('string', 'string', 'integer', 'integer') |
|
109 |
|
110 * 'pass': This check matches everything ! It never fails |
|
111 and the value is unchanged. |
|
112 |
|
113 It is also the default if no check is specified. |
|
114 |
|
115 * 'option': This check matches any from a list of options. |
|
116 You specify this check with : :: |
|
117 |
|
118 option('option 1', 'option 2', 'option 3') |
|
119 |
|
120 You can supply a default value (returned if no value is supplied) |
|
121 using the default keyword argument. |
|
122 |
|
123 You specify a list argument for default using a list constructor syntax in |
|
124 the check : :: |
|
125 |
|
126 checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3')) |
|
127 |
|
128 A badly formatted set of arguments will raise a ``VdtParamError``. |
|
129 """ |
|
130 |
|
131 __version__ = '1.0.1' |
|
132 |
|
133 |
|
134 __all__ = ( |
|
135 '__version__', |
|
136 'dottedQuadToNum', |
|
137 'numToDottedQuad', |
|
138 'ValidateError', |
|
139 'VdtUnknownCheckError', |
|
140 'VdtParamError', |
|
141 'VdtTypeError', |
|
142 'VdtValueError', |
|
143 'VdtValueTooSmallError', |
|
144 'VdtValueTooBigError', |
|
145 'VdtValueTooShortError', |
|
146 'VdtValueTooLongError', |
|
147 'VdtMissingValue', |
|
148 'Validator', |
|
149 'is_integer', |
|
150 'is_float', |
|
151 'is_boolean', |
|
152 'is_list', |
|
153 'is_tuple', |
|
154 'is_ip_addr', |
|
155 'is_string', |
|
156 'is_int_list', |
|
157 'is_bool_list', |
|
158 'is_float_list', |
|
159 'is_string_list', |
|
160 'is_ip_addr_list', |
|
161 'is_mixed_list', |
|
162 'is_option', |
|
163 '__docformat__', |
|
164 ) |
|
165 |
|
166 |
|
167 import re |
|
168 |
|
169 |
|
170 _list_arg = re.compile(r''' |
|
171 (?: |
|
172 ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\( |
|
173 ( |
|
174 (?: |
|
175 \s* |
|
176 (?: |
|
177 (?:".*?")| # double quotes |
|
178 (?:'.*?')| # single quotes |
|
179 (?:[^'",\s\)][^,\)]*?) # unquoted |
|
180 ) |
|
181 \s*,\s* |
|
182 )* |
|
183 (?: |
|
184 (?:".*?")| # double quotes |
|
185 (?:'.*?')| # single quotes |
|
186 (?:[^'",\s\)][^,\)]*?) # unquoted |
|
187 )? # last one |
|
188 ) |
|
189 \) |
|
190 ) |
|
191 ''', re.VERBOSE | re.DOTALL) # two groups |
|
192 |
|
193 _list_members = re.compile(r''' |
|
194 ( |
|
195 (?:".*?")| # double quotes |
|
196 (?:'.*?')| # single quotes |
|
197 (?:[^'",\s=][^,=]*?) # unquoted |
|
198 ) |
|
199 (?: |
|
200 (?:\s*,\s*)|(?:\s*$) # comma |
|
201 ) |
|
202 ''', re.VERBOSE | re.DOTALL) # one group |
|
203 |
|
204 _paramstring = r''' |
|
205 (?: |
|
206 ( |
|
207 (?: |
|
208 [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\( |
|
209 (?: |
|
210 \s* |
|
211 (?: |
|
212 (?:".*?")| # double quotes |
|
213 (?:'.*?')| # single quotes |
|
214 (?:[^'",\s\)][^,\)]*?) # unquoted |
|
215 ) |
|
216 \s*,\s* |
|
217 )* |
|
218 (?: |
|
219 (?:".*?")| # double quotes |
|
220 (?:'.*?')| # single quotes |
|
221 (?:[^'",\s\)][^,\)]*?) # unquoted |
|
222 )? # last one |
|
223 \) |
|
224 )| |
|
225 (?: |
|
226 (?:".*?")| # double quotes |
|
227 (?:'.*?')| # single quotes |
|
228 (?:[^'",\s=][^,=]*?)| # unquoted |
|
229 (?: # keyword argument |
|
230 [a-zA-Z_][a-zA-Z0-9_]*\s*=\s* |
|
231 (?: |
|
232 (?:".*?")| # double quotes |
|
233 (?:'.*?')| # single quotes |
|
234 (?:[^'",\s=][^,=]*?) # unquoted |
|
235 ) |
|
236 ) |
|
237 ) |
|
238 ) |
|
239 (?: |
|
240 (?:\s*,\s*)|(?:\s*$) # comma |
|
241 ) |
|
242 ) |
|
243 ''' |
|
244 |
|
245 _matchstring = '^%s*' % _paramstring |
|
246 |
|
247 # Python pre 2.2.1 doesn't have bool |
|
248 try: |
|
249 bool |
|
250 except NameError: |
|
251 def bool(val): |
|
252 """Simple boolean equivalent function. """ |
|
253 if val: |
|
254 return 1 |
|
255 else: |
|
256 return 0 |
|
257 |
|
258 |
|
259 def dottedQuadToNum(ip): |
|
260 """ |
|
261 Convert decimal dotted quad string to long integer |
|
262 |
|
263 >>> int(dottedQuadToNum('1 ')) |
|
264 1 |
|
265 >>> int(dottedQuadToNum(' 1.2')) |
|
266 16777218 |
|
267 >>> int(dottedQuadToNum(' 1.2.3 ')) |
|
268 16908291 |
|
269 >>> int(dottedQuadToNum('1.2.3.4')) |
|
270 16909060 |
|
271 >>> dottedQuadToNum('255.255.255.255') |
|
272 4294967295L |
|
273 >>> dottedQuadToNum('255.255.255.256') |
|
274 Traceback (most recent call last): |
|
275 ValueError: Not a good dotted-quad IP: 255.255.255.256 |
|
276 """ |
|
277 |
|
278 # import here to avoid it when ip_addr values are not used |
|
279 import socket, struct |
|
280 |
|
281 try: |
|
282 return struct.unpack('!L', |
|
283 socket.inet_aton(ip.strip()))[0] |
|
284 except socket.error: |
|
285 # bug in inet_aton, corrected in Python 2.4 |
|
286 if ip.strip() == '255.255.255.255': |
|
287 return 0xFFFFFFFFL |
|
288 else: |
|
289 raise ValueError('Not a good dotted-quad IP: %s' % ip) |
|
290 return |
|
291 |
|
292 |
|
293 def numToDottedQuad(num): |
|
294 """ |
|
295 Convert long int to dotted quad string |
|
296 |
|
297 >>> numToDottedQuad(-1L) |
|
298 Traceback (most recent call last): |
|
299 ValueError: Not a good numeric IP: -1 |
|
300 >>> numToDottedQuad(1L) |
|
301 '0.0.0.1' |
|
302 >>> numToDottedQuad(16777218L) |
|
303 '1.0.0.2' |
|
304 >>> numToDottedQuad(16908291L) |
|
305 '1.2.0.3' |
|
306 >>> numToDottedQuad(16909060L) |
|
307 '1.2.3.4' |
|
308 >>> numToDottedQuad(4294967295L) |
|
309 '255.255.255.255' |
|
310 >>> numToDottedQuad(4294967296L) |
|
311 Traceback (most recent call last): |
|
312 ValueError: Not a good numeric IP: 4294967296 |
|
313 """ |
|
314 |
|
315 # import here to avoid it when ip_addr values are not used |
|
316 import socket, struct |
|
317 |
|
318 # no need to intercept here, 4294967295L is fine |
|
319 if num > 4294967295L or num < 0: |
|
320 raise ValueError('Not a good numeric IP: %s' % num) |
|
321 try: |
|
322 return socket.inet_ntoa( |
|
323 struct.pack('!L', long(num))) |
|
324 except (socket.error, struct.error, OverflowError): |
|
325 raise ValueError('Not a good numeric IP: %s' % num) |
|
326 |
|
327 |
|
328 class ValidateError(Exception): |
|
329 """ |
|
330 This error indicates that the check failed. |
|
331 It can be the base class for more specific errors. |
|
332 |
|
333 Any check function that fails ought to raise this error. |
|
334 (or a subclass) |
|
335 |
|
336 >>> raise ValidateError |
|
337 Traceback (most recent call last): |
|
338 ValidateError |
|
339 """ |
|
340 |
|
341 |
|
342 class VdtMissingValue(ValidateError): |
|
343 """No value was supplied to a check that needed one.""" |
|
344 |
|
345 |
|
346 class VdtUnknownCheckError(ValidateError): |
|
347 """An unknown check function was requested""" |
|
348 |
|
349 def __init__(self, value): |
|
350 """ |
|
351 >>> raise VdtUnknownCheckError('yoda') |
|
352 Traceback (most recent call last): |
|
353 VdtUnknownCheckError: the check "yoda" is unknown. |
|
354 """ |
|
355 ValidateError.__init__(self, 'the check "%s" is unknown.' % (value,)) |
|
356 |
|
357 |
|
358 class VdtParamError(SyntaxError): |
|
359 """An incorrect parameter was passed""" |
|
360 |
|
361 def __init__(self, name, value): |
|
362 """ |
|
363 >>> raise VdtParamError('yoda', 'jedi') |
|
364 Traceback (most recent call last): |
|
365 VdtParamError: passed an incorrect value "jedi" for parameter "yoda". |
|
366 """ |
|
367 SyntaxError.__init__(self, 'passed an incorrect value "%s" for parameter "%s".' % (value, name)) |
|
368 |
|
369 |
|
370 class VdtTypeError(ValidateError): |
|
371 """The value supplied was of the wrong type""" |
|
372 |
|
373 def __init__(self, value): |
|
374 """ |
|
375 >>> raise VdtTypeError('jedi') |
|
376 Traceback (most recent call last): |
|
377 VdtTypeError: the value "jedi" is of the wrong type. |
|
378 """ |
|
379 ValidateError.__init__(self, 'the value "%s" is of the wrong type.' % (value,)) |
|
380 |
|
381 |
|
382 class VdtValueError(ValidateError): |
|
383 """The value supplied was of the correct type, but was not an allowed value.""" |
|
384 |
|
385 def __init__(self, value): |
|
386 """ |
|
387 >>> raise VdtValueError('jedi') |
|
388 Traceback (most recent call last): |
|
389 VdtValueError: the value "jedi" is unacceptable. |
|
390 """ |
|
391 ValidateError.__init__(self, 'the value "%s" is unacceptable.' % (value,)) |
|
392 |
|
393 |
|
394 class VdtValueTooSmallError(VdtValueError): |
|
395 """The value supplied was of the correct type, but was too small.""" |
|
396 |
|
397 def __init__(self, value): |
|
398 """ |
|
399 >>> raise VdtValueTooSmallError('0') |
|
400 Traceback (most recent call last): |
|
401 VdtValueTooSmallError: the value "0" is too small. |
|
402 """ |
|
403 ValidateError.__init__(self, 'the value "%s" is too small.' % (value,)) |
|
404 |
|
405 |
|
406 class VdtValueTooBigError(VdtValueError): |
|
407 """The value supplied was of the correct type, but was too big.""" |
|
408 |
|
409 def __init__(self, value): |
|
410 """ |
|
411 >>> raise VdtValueTooBigError('1') |
|
412 Traceback (most recent call last): |
|
413 VdtValueTooBigError: the value "1" is too big. |
|
414 """ |
|
415 ValidateError.__init__(self, 'the value "%s" is too big.' % (value,)) |
|
416 |
|
417 |
|
418 class VdtValueTooShortError(VdtValueError): |
|
419 """The value supplied was of the correct type, but was too short.""" |
|
420 |
|
421 def __init__(self, value): |
|
422 """ |
|
423 >>> raise VdtValueTooShortError('jed') |
|
424 Traceback (most recent call last): |
|
425 VdtValueTooShortError: the value "jed" is too short. |
|
426 """ |
|
427 ValidateError.__init__( |
|
428 self, |
|
429 'the value "%s" is too short.' % (value,)) |
|
430 |
|
431 |
|
432 class VdtValueTooLongError(VdtValueError): |
|
433 """The value supplied was of the correct type, but was too long.""" |
|
434 |
|
435 def __init__(self, value): |
|
436 """ |
|
437 >>> raise VdtValueTooLongError('jedie') |
|
438 Traceback (most recent call last): |
|
439 VdtValueTooLongError: the value "jedie" is too long. |
|
440 """ |
|
441 ValidateError.__init__(self, 'the value "%s" is too long.' % (value,)) |
|
442 |
|
443 |
|
444 class Validator(object): |
|
445 """ |
|
446 Validator is an object that allows you to register a set of 'checks'. |
|
447 These checks take input and test that it conforms to the check. |
|
448 |
|
449 This can also involve converting the value from a string into |
|
450 the correct datatype. |
|
451 |
|
452 The ``check`` method takes an input string which configures which |
|
453 check is to be used and applies that check to a supplied value. |
|
454 |
|
455 An example input string would be: |
|
456 'int_range(param1, param2)' |
|
457 |
|
458 You would then provide something like: |
|
459 |
|
460 >>> def int_range_check(value, min, max): |
|
461 ... # turn min and max from strings to integers |
|
462 ... min = int(min) |
|
463 ... max = int(max) |
|
464 ... # check that value is of the correct type. |
|
465 ... # possible valid inputs are integers or strings |
|
466 ... # that represent integers |
|
467 ... if not isinstance(value, (int, long, basestring)): |
|
468 ... raise VdtTypeError(value) |
|
469 ... elif isinstance(value, basestring): |
|
470 ... # if we are given a string |
|
471 ... # attempt to convert to an integer |
|
472 ... try: |
|
473 ... value = int(value) |
|
474 ... except ValueError: |
|
475 ... raise VdtValueError(value) |
|
476 ... # check the value is between our constraints |
|
477 ... if not min <= value: |
|
478 ... raise VdtValueTooSmallError(value) |
|
479 ... if not value <= max: |
|
480 ... raise VdtValueTooBigError(value) |
|
481 ... return value |
|
482 |
|
483 >>> fdict = {'int_range': int_range_check} |
|
484 >>> vtr1 = Validator(fdict) |
|
485 >>> vtr1.check('int_range(20, 40)', '30') |
|
486 30 |
|
487 >>> vtr1.check('int_range(20, 40)', '60') |
|
488 Traceback (most recent call last): |
|
489 VdtValueTooBigError: the value "60" is too big. |
|
490 |
|
491 New functions can be added with : :: |
|
492 |
|
493 >>> vtr2 = Validator() |
|
494 >>> vtr2.functions['int_range'] = int_range_check |
|
495 |
|
496 Or by passing in a dictionary of functions when Validator |
|
497 is instantiated. |
|
498 |
|
499 Your functions *can* use keyword arguments, |
|
500 but the first argument should always be 'value'. |
|
501 |
|
502 If the function doesn't take additional arguments, |
|
503 the parentheses are optional in the check. |
|
504 It can be written with either of : :: |
|
505 |
|
506 keyword = function_name |
|
507 keyword = function_name() |
|
508 |
|
509 The first program to utilise Validator() was Michael Foord's |
|
510 ConfigObj, an alternative to ConfigParser which supports lists and |
|
511 can validate a config file using a config schema. |
|
512 For more details on using Validator with ConfigObj see: |
|
513 http://www.voidspace.org.uk/python/configobj.html |
|
514 """ |
|
515 |
|
516 # this regex does the initial parsing of the checks |
|
517 _func_re = re.compile(r'(.+?)\((.*)\)', re.DOTALL) |
|
518 |
|
519 # this regex takes apart keyword arguments |
|
520 _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$', re.DOTALL) |
|
521 |
|
522 |
|
523 # this regex finds keyword=list(....) type values |
|
524 _list_arg = _list_arg |
|
525 |
|
526 # this regex takes individual values out of lists - in one pass |
|
527 _list_members = _list_members |
|
528 |
|
529 # These regexes check a set of arguments for validity |
|
530 # and then pull the members out |
|
531 _paramfinder = re.compile(_paramstring, re.VERBOSE | re.DOTALL) |
|
532 _matchfinder = re.compile(_matchstring, re.VERBOSE | re.DOTALL) |
|
533 |
|
534 |
|
535 def __init__(self, functions=None): |
|
536 """ |
|
537 >>> vtri = Validator() |
|
538 """ |
|
539 self.functions = { |
|
540 '': self._pass, |
|
541 'integer': is_integer, |
|
542 'float': is_float, |
|
543 'boolean': is_boolean, |
|
544 'ip_addr': is_ip_addr, |
|
545 'string': is_string, |
|
546 'list': is_list, |
|
547 'tuple': is_tuple, |
|
548 'int_list': is_int_list, |
|
549 'float_list': is_float_list, |
|
550 'bool_list': is_bool_list, |
|
551 'ip_addr_list': is_ip_addr_list, |
|
552 'string_list': is_string_list, |
|
553 'mixed_list': is_mixed_list, |
|
554 'pass': self._pass, |
|
555 'option': is_option, |
|
556 'force_list': force_list, |
|
557 } |
|
558 if functions is not None: |
|
559 self.functions.update(functions) |
|
560 # tekNico: for use by ConfigObj |
|
561 self.baseErrorClass = ValidateError |
|
562 self._cache = {} |
|
563 |
|
564 |
|
565 def check(self, check, value, missing=False): |
|
566 """ |
|
567 Usage: check(check, value) |
|
568 |
|
569 Arguments: |
|
570 check: string representing check to apply (including arguments) |
|
571 value: object to be checked |
|
572 Returns value, converted to correct type if necessary |
|
573 |
|
574 If the check fails, raises a ``ValidateError`` subclass. |
|
575 |
|
576 >>> vtor.check('yoda', '') |
|
577 Traceback (most recent call last): |
|
578 VdtUnknownCheckError: the check "yoda" is unknown. |
|
579 >>> vtor.check('yoda()', '') |
|
580 Traceback (most recent call last): |
|
581 VdtUnknownCheckError: the check "yoda" is unknown. |
|
582 |
|
583 >>> vtor.check('string(default="")', '', missing=True) |
|
584 '' |
|
585 """ |
|
586 fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) |
|
587 |
|
588 if missing: |
|
589 if default is None: |
|
590 # no information needed here - to be handled by caller |
|
591 raise VdtMissingValue() |
|
592 value = self._handle_none(default) |
|
593 |
|
594 if value is None: |
|
595 return None |
|
596 |
|
597 return self._check_value(value, fun_name, fun_args, fun_kwargs) |
|
598 |
|
599 |
|
600 def _handle_none(self, value): |
|
601 if value == 'None': |
|
602 return None |
|
603 elif value in ("'None'", '"None"'): |
|
604 # Special case a quoted None |
|
605 value = self._unquote(value) |
|
606 return value |
|
607 |
|
608 |
|
609 def _parse_with_caching(self, check): |
|
610 if check in self._cache: |
|
611 fun_name, fun_args, fun_kwargs, default = self._cache[check] |
|
612 # We call list and dict below to work with *copies* of the data |
|
613 # rather than the original (which are mutable of course) |
|
614 fun_args = list(fun_args) |
|
615 fun_kwargs = dict(fun_kwargs) |
|
616 else: |
|
617 fun_name, fun_args, fun_kwargs, default = self._parse_check(check) |
|
618 fun_kwargs = dict([(str(key), value) for (key, value) in fun_kwargs.items()]) |
|
619 self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default |
|
620 return fun_name, fun_args, fun_kwargs, default |
|
621 |
|
622 |
|
623 def _check_value(self, value, fun_name, fun_args, fun_kwargs): |
|
624 try: |
|
625 fun = self.functions[fun_name] |
|
626 except KeyError: |
|
627 raise VdtUnknownCheckError(fun_name) |
|
628 else: |
|
629 return fun(value, *fun_args, **fun_kwargs) |
|
630 |
|
631 |
|
632 def _parse_check(self, check): |
|
633 fun_match = self._func_re.match(check) |
|
634 if fun_match: |
|
635 fun_name = fun_match.group(1) |
|
636 arg_string = fun_match.group(2) |
|
637 arg_match = self._matchfinder.match(arg_string) |
|
638 if arg_match is None: |
|
639 # Bad syntax |
|
640 raise VdtParamError('Bad syntax in check "%s".' % check) |
|
641 fun_args = [] |
|
642 fun_kwargs = {} |
|
643 # pull out args of group 2 |
|
644 for arg in self._paramfinder.findall(arg_string): |
|
645 # args may need whitespace removing (before removing quotes) |
|
646 arg = arg.strip() |
|
647 listmatch = self._list_arg.match(arg) |
|
648 if listmatch: |
|
649 key, val = self._list_handle(listmatch) |
|
650 fun_kwargs[key] = val |
|
651 continue |
|
652 keymatch = self._key_arg.match(arg) |
|
653 if keymatch: |
|
654 val = keymatch.group(2) |
|
655 if not val in ("'None'", '"None"'): |
|
656 # Special case a quoted None |
|
657 val = self._unquote(val) |
|
658 fun_kwargs[keymatch.group(1)] = val |
|
659 continue |
|
660 |
|
661 fun_args.append(self._unquote(arg)) |
|
662 else: |
|
663 # allows for function names without (args) |
|
664 return check, (), {}, None |
|
665 |
|
666 # Default must be deleted if the value is specified too, |
|
667 # otherwise the check function will get a spurious "default" keyword arg |
|
668 default = fun_kwargs.pop('default', None) |
|
669 return fun_name, fun_args, fun_kwargs, default |
|
670 |
|
671 |
|
672 def _unquote(self, val): |
|
673 """Unquote a value if necessary.""" |
|
674 if (len(val) >= 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]): |
|
675 val = val[1:-1] |
|
676 return val |
|
677 |
|
678 |
|
679 def _list_handle(self, listmatch): |
|
680 """Take apart a ``keyword=list('val, 'val')`` type string.""" |
|
681 out = [] |
|
682 name = listmatch.group(1) |
|
683 args = listmatch.group(2) |
|
684 for arg in self._list_members.findall(args): |
|
685 out.append(self._unquote(arg)) |
|
686 return name, out |
|
687 |
|
688 |
|
689 def _pass(self, value): |
|
690 """ |
|
691 Dummy check that always passes |
|
692 |
|
693 >>> vtor.check('', 0) |
|
694 0 |
|
695 >>> vtor.check('', '0') |
|
696 '0' |
|
697 """ |
|
698 return value |
|
699 |
|
700 |
|
701 def get_default_value(self, check): |
|
702 """ |
|
703 Given a check, return the default value for the check |
|
704 (converted to the right type). |
|
705 |
|
706 If the check doesn't specify a default value then a |
|
707 ``KeyError`` will be raised. |
|
708 """ |
|
709 fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check) |
|
710 if default is None: |
|
711 raise KeyError('Check "%s" has no default value.' % check) |
|
712 value = self._handle_none(default) |
|
713 if value is None: |
|
714 return value |
|
715 return self._check_value(value, fun_name, fun_args, fun_kwargs) |
|
716 |
|
717 |
|
718 def _is_num_param(names, values, to_float=False): |
|
719 """ |
|
720 Return numbers from inputs or raise VdtParamError. |
|
721 |
|
722 Lets ``None`` pass through. |
|
723 Pass in keyword argument ``to_float=True`` to |
|
724 use float for the conversion rather than int. |
|
725 |
|
726 >>> _is_num_param(('', ''), (0, 1.0)) |
|
727 [0, 1] |
|
728 >>> _is_num_param(('', ''), (0, 1.0), to_float=True) |
|
729 [0.0, 1.0] |
|
730 >>> _is_num_param(('a'), ('a')) |
|
731 Traceback (most recent call last): |
|
732 VdtParamError: passed an incorrect value "a" for parameter "a". |
|
733 """ |
|
734 fun = to_float and float or int |
|
735 out_params = [] |
|
736 for (name, val) in zip(names, values): |
|
737 if val is None: |
|
738 out_params.append(val) |
|
739 elif isinstance(val, (int, long, float, basestring)): |
|
740 try: |
|
741 out_params.append(fun(val)) |
|
742 except ValueError, e: |
|
743 raise VdtParamError(name, val) |
|
744 else: |
|
745 raise VdtParamError(name, val) |
|
746 return out_params |
|
747 |
|
748 |
|
749 # built in checks |
|
750 # you can override these by setting the appropriate name |
|
751 # in Validator.functions |
|
752 # note: if the params are specified wrongly in your input string, |
|
753 # you will also raise errors. |
|
754 |
|
755 def is_integer(value, min=None, max=None): |
|
756 """ |
|
757 A check that tests that a given value is an integer (int, or long) |
|
758 and optionally, between bounds. A negative value is accepted, while |
|
759 a float will fail. |
|
760 |
|
761 If the value is a string, then the conversion is done - if possible. |
|
762 Otherwise a VdtError is raised. |
|
763 |
|
764 >>> vtor.check('integer', '-1') |
|
765 -1 |
|
766 >>> vtor.check('integer', '0') |
|
767 0 |
|
768 >>> vtor.check('integer', 9) |
|
769 9 |
|
770 >>> vtor.check('integer', 'a') |
|
771 Traceback (most recent call last): |
|
772 VdtTypeError: the value "a" is of the wrong type. |
|
773 >>> vtor.check('integer', '2.2') |
|
774 Traceback (most recent call last): |
|
775 VdtTypeError: the value "2.2" is of the wrong type. |
|
776 >>> vtor.check('integer(10)', '20') |
|
777 20 |
|
778 >>> vtor.check('integer(max=20)', '15') |
|
779 15 |
|
780 >>> vtor.check('integer(10)', '9') |
|
781 Traceback (most recent call last): |
|
782 VdtValueTooSmallError: the value "9" is too small. |
|
783 >>> vtor.check('integer(10)', 9) |
|
784 Traceback (most recent call last): |
|
785 VdtValueTooSmallError: the value "9" is too small. |
|
786 >>> vtor.check('integer(max=20)', '35') |
|
787 Traceback (most recent call last): |
|
788 VdtValueTooBigError: the value "35" is too big. |
|
789 >>> vtor.check('integer(max=20)', 35) |
|
790 Traceback (most recent call last): |
|
791 VdtValueTooBigError: the value "35" is too big. |
|
792 >>> vtor.check('integer(0, 9)', False) |
|
793 0 |
|
794 """ |
|
795 (min_val, max_val) = _is_num_param(('min', 'max'), (min, max)) |
|
796 if not isinstance(value, (int, long, basestring)): |
|
797 raise VdtTypeError(value) |
|
798 if isinstance(value, basestring): |
|
799 # if it's a string - does it represent an integer ? |
|
800 try: |
|
801 value = int(value) |
|
802 except ValueError: |
|
803 raise VdtTypeError(value) |
|
804 if (min_val is not None) and (value < min_val): |
|
805 raise VdtValueTooSmallError(value) |
|
806 if (max_val is not None) and (value > max_val): |
|
807 raise VdtValueTooBigError(value) |
|
808 return value |
|
809 |
|
810 |
|
811 def is_float(value, min=None, max=None): |
|
812 """ |
|
813 A check that tests that a given value is a float |
|
814 (an integer will be accepted), and optionally - that it is between bounds. |
|
815 |
|
816 If the value is a string, then the conversion is done - if possible. |
|
817 Otherwise a VdtError is raised. |
|
818 |
|
819 This can accept negative values. |
|
820 |
|
821 >>> vtor.check('float', '2') |
|
822 2.0 |
|
823 |
|
824 From now on we multiply the value to avoid comparing decimals |
|
825 |
|
826 >>> vtor.check('float', '-6.8') * 10 |
|
827 -68.0 |
|
828 >>> vtor.check('float', '12.2') * 10 |
|
829 122.0 |
|
830 >>> vtor.check('float', 8.4) * 10 |
|
831 84.0 |
|
832 >>> vtor.check('float', 'a') |
|
833 Traceback (most recent call last): |
|
834 VdtTypeError: the value "a" is of the wrong type. |
|
835 >>> vtor.check('float(10.1)', '10.2') * 10 |
|
836 102.0 |
|
837 >>> vtor.check('float(max=20.2)', '15.1') * 10 |
|
838 151.0 |
|
839 >>> vtor.check('float(10.0)', '9.0') |
|
840 Traceback (most recent call last): |
|
841 VdtValueTooSmallError: the value "9.0" is too small. |
|
842 >>> vtor.check('float(max=20.0)', '35.0') |
|
843 Traceback (most recent call last): |
|
844 VdtValueTooBigError: the value "35.0" is too big. |
|
845 """ |
|
846 (min_val, max_val) = _is_num_param( |
|
847 ('min', 'max'), (min, max), to_float=True) |
|
848 if not isinstance(value, (int, long, float, basestring)): |
|
849 raise VdtTypeError(value) |
|
850 if not isinstance(value, float): |
|
851 # if it's a string - does it represent a float ? |
|
852 try: |
|
853 value = float(value) |
|
854 except ValueError: |
|
855 raise VdtTypeError(value) |
|
856 if (min_val is not None) and (value < min_val): |
|
857 raise VdtValueTooSmallError(value) |
|
858 if (max_val is not None) and (value > max_val): |
|
859 raise VdtValueTooBigError(value) |
|
860 return value |
|
861 |
|
862 |
|
863 bool_dict = { |
|
864 True: True, 'on': True, '1': True, 'true': True, 'yes': True, |
|
865 False: False, 'off': False, '0': False, 'false': False, 'no': False, |
|
866 } |
|
867 |
|
868 |
|
869 def is_boolean(value): |
|
870 """ |
|
871 Check if the value represents a boolean. |
|
872 |
|
873 >>> vtor.check('boolean', 0) |
|
874 0 |
|
875 >>> vtor.check('boolean', False) |
|
876 0 |
|
877 >>> vtor.check('boolean', '0') |
|
878 0 |
|
879 >>> vtor.check('boolean', 'off') |
|
880 0 |
|
881 >>> vtor.check('boolean', 'false') |
|
882 0 |
|
883 >>> vtor.check('boolean', 'no') |
|
884 0 |
|
885 >>> vtor.check('boolean', 'nO') |
|
886 0 |
|
887 >>> vtor.check('boolean', 'NO') |
|
888 0 |
|
889 >>> vtor.check('boolean', 1) |
|
890 1 |
|
891 >>> vtor.check('boolean', True) |
|
892 1 |
|
893 >>> vtor.check('boolean', '1') |
|
894 1 |
|
895 >>> vtor.check('boolean', 'on') |
|
896 1 |
|
897 >>> vtor.check('boolean', 'true') |
|
898 1 |
|
899 >>> vtor.check('boolean', 'yes') |
|
900 1 |
|
901 >>> vtor.check('boolean', 'Yes') |
|
902 1 |
|
903 >>> vtor.check('boolean', 'YES') |
|
904 1 |
|
905 >>> vtor.check('boolean', '') |
|
906 Traceback (most recent call last): |
|
907 VdtTypeError: the value "" is of the wrong type. |
|
908 >>> vtor.check('boolean', 'up') |
|
909 Traceback (most recent call last): |
|
910 VdtTypeError: the value "up" is of the wrong type. |
|
911 |
|
912 """ |
|
913 if isinstance(value, basestring): |
|
914 try: |
|
915 return bool_dict[value.lower()] |
|
916 except KeyError: |
|
917 raise VdtTypeError(value) |
|
918 # we do an equality test rather than an identity test |
|
919 # this ensures Python 2.2 compatibilty |
|
920 # and allows 0 and 1 to represent True and False |
|
921 if value == False: |
|
922 return False |
|
923 elif value == True: |
|
924 return True |
|
925 else: |
|
926 raise VdtTypeError(value) |
|
927 |
|
928 |
|
929 def is_ip_addr(value): |
|
930 """ |
|
931 Check that the supplied value is an Internet Protocol address, v.4, |
|
932 represented by a dotted-quad string, i.e. '1.2.3.4'. |
|
933 |
|
934 >>> vtor.check('ip_addr', '1 ') |
|
935 '1' |
|
936 >>> vtor.check('ip_addr', ' 1.2') |
|
937 '1.2' |
|
938 >>> vtor.check('ip_addr', ' 1.2.3 ') |
|
939 '1.2.3' |
|
940 >>> vtor.check('ip_addr', '1.2.3.4') |
|
941 '1.2.3.4' |
|
942 >>> vtor.check('ip_addr', '0.0.0.0') |
|
943 '0.0.0.0' |
|
944 >>> vtor.check('ip_addr', '255.255.255.255') |
|
945 '255.255.255.255' |
|
946 >>> vtor.check('ip_addr', '255.255.255.256') |
|
947 Traceback (most recent call last): |
|
948 VdtValueError: the value "255.255.255.256" is unacceptable. |
|
949 >>> vtor.check('ip_addr', '1.2.3.4.5') |
|
950 Traceback (most recent call last): |
|
951 VdtValueError: the value "1.2.3.4.5" is unacceptable. |
|
952 >>> vtor.check('ip_addr', 0) |
|
953 Traceback (most recent call last): |
|
954 VdtTypeError: the value "0" is of the wrong type. |
|
955 """ |
|
956 if not isinstance(value, basestring): |
|
957 raise VdtTypeError(value) |
|
958 value = value.strip() |
|
959 try: |
|
960 dottedQuadToNum(value) |
|
961 except ValueError: |
|
962 raise VdtValueError(value) |
|
963 return value |
|
964 |
|
965 |
|
966 def is_list(value, min=None, max=None): |
|
967 """ |
|
968 Check that the value is a list of values. |
|
969 |
|
970 You can optionally specify the minimum and maximum number of members. |
|
971 |
|
972 It does no check on list members. |
|
973 |
|
974 >>> vtor.check('list', ()) |
|
975 [] |
|
976 >>> vtor.check('list', []) |
|
977 [] |
|
978 >>> vtor.check('list', (1, 2)) |
|
979 [1, 2] |
|
980 >>> vtor.check('list', [1, 2]) |
|
981 [1, 2] |
|
982 >>> vtor.check('list(3)', (1, 2)) |
|
983 Traceback (most recent call last): |
|
984 VdtValueTooShortError: the value "(1, 2)" is too short. |
|
985 >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6)) |
|
986 Traceback (most recent call last): |
|
987 VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. |
|
988 >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4)) |
|
989 [1, 2, 3, 4] |
|
990 >>> vtor.check('list', 0) |
|
991 Traceback (most recent call last): |
|
992 VdtTypeError: the value "0" is of the wrong type. |
|
993 >>> vtor.check('list', '12') |
|
994 Traceback (most recent call last): |
|
995 VdtTypeError: the value "12" is of the wrong type. |
|
996 """ |
|
997 (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) |
|
998 if isinstance(value, basestring): |
|
999 raise VdtTypeError(value) |
|
1000 try: |
|
1001 num_members = len(value) |
|
1002 except TypeError: |
|
1003 raise VdtTypeError(value) |
|
1004 if min_len is not None and num_members < min_len: |
|
1005 raise VdtValueTooShortError(value) |
|
1006 if max_len is not None and num_members > max_len: |
|
1007 raise VdtValueTooLongError(value) |
|
1008 return list(value) |
|
1009 |
|
1010 |
|
1011 def is_tuple(value, min=None, max=None): |
|
1012 """ |
|
1013 Check that the value is a tuple of values. |
|
1014 |
|
1015 You can optionally specify the minimum and maximum number of members. |
|
1016 |
|
1017 It does no check on members. |
|
1018 |
|
1019 >>> vtor.check('tuple', ()) |
|
1020 () |
|
1021 >>> vtor.check('tuple', []) |
|
1022 () |
|
1023 >>> vtor.check('tuple', (1, 2)) |
|
1024 (1, 2) |
|
1025 >>> vtor.check('tuple', [1, 2]) |
|
1026 (1, 2) |
|
1027 >>> vtor.check('tuple(3)', (1, 2)) |
|
1028 Traceback (most recent call last): |
|
1029 VdtValueTooShortError: the value "(1, 2)" is too short. |
|
1030 >>> vtor.check('tuple(max=5)', (1, 2, 3, 4, 5, 6)) |
|
1031 Traceback (most recent call last): |
|
1032 VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long. |
|
1033 >>> vtor.check('tuple(min=3, max=5)', (1, 2, 3, 4)) |
|
1034 (1, 2, 3, 4) |
|
1035 >>> vtor.check('tuple', 0) |
|
1036 Traceback (most recent call last): |
|
1037 VdtTypeError: the value "0" is of the wrong type. |
|
1038 >>> vtor.check('tuple', '12') |
|
1039 Traceback (most recent call last): |
|
1040 VdtTypeError: the value "12" is of the wrong type. |
|
1041 """ |
|
1042 return tuple(is_list(value, min, max)) |
|
1043 |
|
1044 |
|
1045 def is_string(value, min=None, max=None): |
|
1046 """ |
|
1047 Check that the supplied value is a string. |
|
1048 |
|
1049 You can optionally specify the minimum and maximum number of members. |
|
1050 |
|
1051 >>> vtor.check('string', '0') |
|
1052 '0' |
|
1053 >>> vtor.check('string', 0) |
|
1054 Traceback (most recent call last): |
|
1055 VdtTypeError: the value "0" is of the wrong type. |
|
1056 >>> vtor.check('string(2)', '12') |
|
1057 '12' |
|
1058 >>> vtor.check('string(2)', '1') |
|
1059 Traceback (most recent call last): |
|
1060 VdtValueTooShortError: the value "1" is too short. |
|
1061 >>> vtor.check('string(min=2, max=3)', '123') |
|
1062 '123' |
|
1063 >>> vtor.check('string(min=2, max=3)', '1234') |
|
1064 Traceback (most recent call last): |
|
1065 VdtValueTooLongError: the value "1234" is too long. |
|
1066 """ |
|
1067 if not isinstance(value, basestring): |
|
1068 raise VdtTypeError(value) |
|
1069 (min_len, max_len) = _is_num_param(('min', 'max'), (min, max)) |
|
1070 try: |
|
1071 num_members = len(value) |
|
1072 except TypeError: |
|
1073 raise VdtTypeError(value) |
|
1074 if min_len is not None and num_members < min_len: |
|
1075 raise VdtValueTooShortError(value) |
|
1076 if max_len is not None and num_members > max_len: |
|
1077 raise VdtValueTooLongError(value) |
|
1078 return value |
|
1079 |
|
1080 |
|
1081 def is_int_list(value, min=None, max=None): |
|
1082 """ |
|
1083 Check that the value is a list of integers. |
|
1084 |
|
1085 You can optionally specify the minimum and maximum number of members. |
|
1086 |
|
1087 Each list member is checked that it is an integer. |
|
1088 |
|
1089 >>> vtor.check('int_list', ()) |
|
1090 [] |
|
1091 >>> vtor.check('int_list', []) |
|
1092 [] |
|
1093 >>> vtor.check('int_list', (1, 2)) |
|
1094 [1, 2] |
|
1095 >>> vtor.check('int_list', [1, 2]) |
|
1096 [1, 2] |
|
1097 >>> vtor.check('int_list', [1, 'a']) |
|
1098 Traceback (most recent call last): |
|
1099 VdtTypeError: the value "a" is of the wrong type. |
|
1100 """ |
|
1101 return [is_integer(mem) for mem in is_list(value, min, max)] |
|
1102 |
|
1103 |
|
1104 def is_bool_list(value, min=None, max=None): |
|
1105 """ |
|
1106 Check that the value is a list of booleans. |
|
1107 |
|
1108 You can optionally specify the minimum and maximum number of members. |
|
1109 |
|
1110 Each list member is checked that it is a boolean. |
|
1111 |
|
1112 >>> vtor.check('bool_list', ()) |
|
1113 [] |
|
1114 >>> vtor.check('bool_list', []) |
|
1115 [] |
|
1116 >>> check_res = vtor.check('bool_list', (True, False)) |
|
1117 >>> check_res == [True, False] |
|
1118 1 |
|
1119 >>> check_res = vtor.check('bool_list', [True, False]) |
|
1120 >>> check_res == [True, False] |
|
1121 1 |
|
1122 >>> vtor.check('bool_list', [True, 'a']) |
|
1123 Traceback (most recent call last): |
|
1124 VdtTypeError: the value "a" is of the wrong type. |
|
1125 """ |
|
1126 return [is_boolean(mem) for mem in is_list(value, min, max)] |
|
1127 |
|
1128 |
|
1129 def is_float_list(value, min=None, max=None): |
|
1130 """ |
|
1131 Check that the value is a list of floats. |
|
1132 |
|
1133 You can optionally specify the minimum and maximum number of members. |
|
1134 |
|
1135 Each list member is checked that it is a float. |
|
1136 |
|
1137 >>> vtor.check('float_list', ()) |
|
1138 [] |
|
1139 >>> vtor.check('float_list', []) |
|
1140 [] |
|
1141 >>> vtor.check('float_list', (1, 2.0)) |
|
1142 [1.0, 2.0] |
|
1143 >>> vtor.check('float_list', [1, 2.0]) |
|
1144 [1.0, 2.0] |
|
1145 >>> vtor.check('float_list', [1, 'a']) |
|
1146 Traceback (most recent call last): |
|
1147 VdtTypeError: the value "a" is of the wrong type. |
|
1148 """ |
|
1149 return [is_float(mem) for mem in is_list(value, min, max)] |
|
1150 |
|
1151 |
|
1152 def is_string_list(value, min=None, max=None): |
|
1153 """ |
|
1154 Check that the value is a list of strings. |
|
1155 |
|
1156 You can optionally specify the minimum and maximum number of members. |
|
1157 |
|
1158 Each list member is checked that it is a string. |
|
1159 |
|
1160 >>> vtor.check('string_list', ()) |
|
1161 [] |
|
1162 >>> vtor.check('string_list', []) |
|
1163 [] |
|
1164 >>> vtor.check('string_list', ('a', 'b')) |
|
1165 ['a', 'b'] |
|
1166 >>> vtor.check('string_list', ['a', 1]) |
|
1167 Traceback (most recent call last): |
|
1168 VdtTypeError: the value "1" is of the wrong type. |
|
1169 >>> vtor.check('string_list', 'hello') |
|
1170 Traceback (most recent call last): |
|
1171 VdtTypeError: the value "hello" is of the wrong type. |
|
1172 """ |
|
1173 if isinstance(value, basestring): |
|
1174 raise VdtTypeError(value) |
|
1175 return [is_string(mem) for mem in is_list(value, min, max)] |
|
1176 |
|
1177 |
|
1178 def is_ip_addr_list(value, min=None, max=None): |
|
1179 """ |
|
1180 Check that the value is a list of IP addresses. |
|
1181 |
|
1182 You can optionally specify the minimum and maximum number of members. |
|
1183 |
|
1184 Each list member is checked that it is an IP address. |
|
1185 |
|
1186 >>> vtor.check('ip_addr_list', ()) |
|
1187 [] |
|
1188 >>> vtor.check('ip_addr_list', []) |
|
1189 [] |
|
1190 >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8')) |
|
1191 ['1.2.3.4', '5.6.7.8'] |
|
1192 >>> vtor.check('ip_addr_list', ['a']) |
|
1193 Traceback (most recent call last): |
|
1194 VdtValueError: the value "a" is unacceptable. |
|
1195 """ |
|
1196 return [is_ip_addr(mem) for mem in is_list(value, min, max)] |
|
1197 |
|
1198 |
|
1199 def force_list(value, min=None, max=None): |
|
1200 """ |
|
1201 Check that a value is a list, coercing strings into |
|
1202 a list with one member. Useful where users forget the |
|
1203 trailing comma that turns a single value into a list. |
|
1204 |
|
1205 You can optionally specify the minimum and maximum number of members. |
|
1206 A minumum of greater than one will fail if the user only supplies a |
|
1207 string. |
|
1208 |
|
1209 >>> vtor.check('force_list', ()) |
|
1210 [] |
|
1211 >>> vtor.check('force_list', []) |
|
1212 [] |
|
1213 >>> vtor.check('force_list', 'hello') |
|
1214 ['hello'] |
|
1215 """ |
|
1216 if not isinstance(value, (list, tuple)): |
|
1217 value = [value] |
|
1218 return is_list(value, min, max) |
|
1219 |
|
1220 |
|
1221 |
|
1222 fun_dict = { |
|
1223 'integer': is_integer, |
|
1224 'float': is_float, |
|
1225 'ip_addr': is_ip_addr, |
|
1226 'string': is_string, |
|
1227 'boolean': is_boolean, |
|
1228 } |
|
1229 |
|
1230 |
|
1231 def is_mixed_list(value, *args): |
|
1232 """ |
|
1233 Check that the value is a list. |
|
1234 Allow specifying the type of each member. |
|
1235 Work on lists of specific lengths. |
|
1236 |
|
1237 You specify each member as a positional argument specifying type |
|
1238 |
|
1239 Each type should be one of the following strings : |
|
1240 'integer', 'float', 'ip_addr', 'string', 'boolean' |
|
1241 |
|
1242 So you can specify a list of two strings, followed by |
|
1243 two integers as : |
|
1244 |
|
1245 mixed_list('string', 'string', 'integer', 'integer') |
|
1246 |
|
1247 The length of the list must match the number of positional |
|
1248 arguments you supply. |
|
1249 |
|
1250 >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')" |
|
1251 >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True)) |
|
1252 >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] |
|
1253 1 |
|
1254 >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True')) |
|
1255 >>> check_res == [1, 2.0, '1.2.3.4', 'a', True] |
|
1256 1 |
|
1257 >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True)) |
|
1258 Traceback (most recent call last): |
|
1259 VdtTypeError: the value "b" is of the wrong type. |
|
1260 >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a')) |
|
1261 Traceback (most recent call last): |
|
1262 VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short. |
|
1263 >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b')) |
|
1264 Traceback (most recent call last): |
|
1265 VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long. |
|
1266 >>> vtor.check(mix_str, 0) |
|
1267 Traceback (most recent call last): |
|
1268 VdtTypeError: the value "0" is of the wrong type. |
|
1269 |
|
1270 This test requires an elaborate setup, because of a change in error string |
|
1271 output from the interpreter between Python 2.2 and 2.3 . |
|
1272 |
|
1273 >>> res_seq = ( |
|
1274 ... 'passed an incorrect value "', |
|
1275 ... 'yoda', |
|
1276 ... '" for parameter "mixed_list".', |
|
1277 ... ) |
|
1278 >>> res_str = "'".join(res_seq) |
|
1279 >>> try: |
|
1280 ... vtor.check('mixed_list("yoda")', ('a')) |
|
1281 ... except VdtParamError, err: |
|
1282 ... str(err) == res_str |
|
1283 1 |
|
1284 """ |
|
1285 try: |
|
1286 length = len(value) |
|
1287 except TypeError: |
|
1288 raise VdtTypeError(value) |
|
1289 if length < len(args): |
|
1290 raise VdtValueTooShortError(value) |
|
1291 elif length > len(args): |
|
1292 raise VdtValueTooLongError(value) |
|
1293 try: |
|
1294 return [fun_dict[arg](val) for arg, val in zip(args, value)] |
|
1295 except KeyError, e: |
|
1296 raise VdtParamError('mixed_list', e) |
|
1297 |
|
1298 |
|
1299 def is_option(value, *options): |
|
1300 """ |
|
1301 This check matches the value to any of a set of options. |
|
1302 |
|
1303 >>> vtor.check('option("yoda", "jedi")', 'yoda') |
|
1304 'yoda' |
|
1305 >>> vtor.check('option("yoda", "jedi")', 'jed') |
|
1306 Traceback (most recent call last): |
|
1307 VdtValueError: the value "jed" is unacceptable. |
|
1308 >>> vtor.check('option("yoda", "jedi")', 0) |
|
1309 Traceback (most recent call last): |
|
1310 VdtTypeError: the value "0" is of the wrong type. |
|
1311 """ |
|
1312 if not isinstance(value, basestring): |
|
1313 raise VdtTypeError(value) |
|
1314 if not value in options: |
|
1315 raise VdtValueError(value) |
|
1316 return value |
|
1317 |
|
1318 |
|
1319 def _test(value, *args, **keywargs): |
|
1320 """ |
|
1321 A function that exists for test purposes. |
|
1322 |
|
1323 >>> checks = [ |
|
1324 ... '3, 6, min=1, max=3, test=list(a, b, c)', |
|
1325 ... '3', |
|
1326 ... '3, 6', |
|
1327 ... '3,', |
|
1328 ... 'min=1, test="a b c"', |
|
1329 ... 'min=5, test="a, b, c"', |
|
1330 ... 'min=1, max=3, test="a, b, c"', |
|
1331 ... 'min=-100, test=-99', |
|
1332 ... 'min=1, max=3', |
|
1333 ... '3, 6, test="36"', |
|
1334 ... '3, 6, test="a, b, c"', |
|
1335 ... '3, max=3, test=list("a", "b", "c")', |
|
1336 ... '''3, max=3, test=list("'a'", 'b', "x=(c)")''', |
|
1337 ... "test='x=fish(3)'", |
|
1338 ... ] |
|
1339 >>> v = Validator({'test': _test}) |
|
1340 >>> for entry in checks: |
|
1341 ... print v.check(('test(%s)' % entry), 3) |
|
1342 (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'}) |
|
1343 (3, ('3',), {}) |
|
1344 (3, ('3', '6'), {}) |
|
1345 (3, ('3',), {}) |
|
1346 (3, (), {'test': 'a b c', 'min': '1'}) |
|
1347 (3, (), {'test': 'a, b, c', 'min': '5'}) |
|
1348 (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'}) |
|
1349 (3, (), {'test': '-99', 'min': '-100'}) |
|
1350 (3, (), {'max': '3', 'min': '1'}) |
|
1351 (3, ('3', '6'), {'test': '36'}) |
|
1352 (3, ('3', '6'), {'test': 'a, b, c'}) |
|
1353 (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'}) |
|
1354 (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'}) |
|
1355 (3, (), {'test': 'x=fish(3)'}) |
|
1356 |
|
1357 >>> v = Validator() |
|
1358 >>> v.check('integer(default=6)', '3') |
|
1359 3 |
|
1360 >>> v.check('integer(default=6)', None, True) |
|
1361 6 |
|
1362 >>> v.get_default_value('integer(default=6)') |
|
1363 6 |
|
1364 >>> v.get_default_value('float(default=6)') |
|
1365 6.0 |
|
1366 >>> v.get_default_value('pass(default=None)') |
|
1367 >>> v.get_default_value("string(default='None')") |
|
1368 'None' |
|
1369 >>> v.get_default_value('pass') |
|
1370 Traceback (most recent call last): |
|
1371 KeyError: 'Check "pass" has no default value.' |
|
1372 >>> v.get_default_value('pass(default=list(1, 2, 3, 4))') |
|
1373 ['1', '2', '3', '4'] |
|
1374 |
|
1375 >>> v = Validator() |
|
1376 >>> v.check("pass(default=None)", None, True) |
|
1377 >>> v.check("pass(default='None')", None, True) |
|
1378 'None' |
|
1379 >>> v.check('pass(default="None")', None, True) |
|
1380 'None' |
|
1381 >>> v.check('pass(default=list(1, 2, 3, 4))', None, True) |
|
1382 ['1', '2', '3', '4'] |
|
1383 |
|
1384 Bug test for unicode arguments |
|
1385 >>> v = Validator() |
|
1386 >>> v.check(u'string(min=4)', u'test') |
|
1387 u'test' |
|
1388 |
|
1389 >>> v = Validator() |
|
1390 >>> v.get_default_value(u'string(min=4, default="1234")') |
|
1391 u'1234' |
|
1392 >>> v.check(u'string(min=4, default="1234")', u'test') |
|
1393 u'test' |
|
1394 |
|
1395 >>> v = Validator() |
|
1396 >>> default = v.get_default_value('string(default=None)') |
|
1397 >>> default == None |
|
1398 1 |
|
1399 """ |
|
1400 return (value, args, keywargs) |
|
1401 |
|
1402 |
|
1403 def _test2(): |
|
1404 """ |
|
1405 >>> |
|
1406 >>> v = Validator() |
|
1407 >>> v.get_default_value('string(default="#ff00dd")') |
|
1408 '#ff00dd' |
|
1409 >>> v.get_default_value('integer(default=3) # comment') |
|
1410 3 |
|
1411 """ |
|
1412 |
|
1413 def _test3(): |
|
1414 r""" |
|
1415 >>> vtor.check('string(default="")', '', missing=True) |
|
1416 '' |
|
1417 >>> vtor.check('string(default="\n")', '', missing=True) |
|
1418 '\n' |
|
1419 >>> print vtor.check('string(default="\n")', '', missing=True), |
|
1420 <BLANKLINE> |
|
1421 >>> vtor.check('string()', '\n') |
|
1422 '\n' |
|
1423 >>> vtor.check('string(default="\n\n\n")', '', missing=True) |
|
1424 '\n\n\n' |
|
1425 >>> vtor.check('string()', 'random \n text goes here\n\n') |
|
1426 'random \n text goes here\n\n' |
|
1427 >>> vtor.check('string(default=" \nrandom text\ngoes \n here\n\n ")', |
|
1428 ... '', missing=True) |
|
1429 ' \nrandom text\ngoes \n here\n\n ' |
|
1430 >>> vtor.check("string(default='\n\n\n')", '', missing=True) |
|
1431 '\n\n\n' |
|
1432 >>> vtor.check("option('\n','a','b',default='\n')", '', missing=True) |
|
1433 '\n' |
|
1434 >>> vtor.check("string_list()", ['foo', '\n', 'bar']) |
|
1435 ['foo', '\n', 'bar'] |
|
1436 >>> vtor.check("string_list(default=list('\n'))", '', missing=True) |
|
1437 ['\n'] |
|
1438 """ |
|
1439 |
|
1440 |
|
1441 if __name__ == '__main__': |
|
1442 # run the code tests in doctest format |
|
1443 import sys |
|
1444 import doctest |
|
1445 m = sys.modules.get('__main__') |
|
1446 globs = m.__dict__.copy() |
|
1447 globs.update({ |
|
1448 'vtor': Validator(), |
|
1449 }) |
|
1450 doctest.testmod(m, globs=globs) |