blob: 9da84aceee9c7ba8ee96f2c69edb402150baa731 [file] [log] [blame]
adminbae64d82013-08-01 10:50:15 -07001# 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"""
Jon Hall4ba53f02015-07-29 13:07:41 -070020 The Validator object is used to check that supplied values
adminbae64d82013-08-01 10:50:15 -070021 conform to a specification.
Jon Hall4ba53f02015-07-29 13:07:41 -070022
adminbae64d82013-08-01 10:50:15 -070023 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.
Jon Hall4ba53f02015-07-29 13:07:41 -070029
adminbae64d82013-08-01 10:50:15 -070030 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.
Jon Hall4ba53f02015-07-29 13:07:41 -070034
adminbae64d82013-08-01 10:50:15 -070035 The standard functions work with the following basic data types :
Jon Hall4ba53f02015-07-29 13:07:41 -070036
adminbae64d82013-08-01 10:50:15 -070037 * integers
38 * floats
39 * booleans
40 * strings
41 * ip_addr
Jon Hall4ba53f02015-07-29 13:07:41 -070042
adminbae64d82013-08-01 10:50:15 -070043 plus lists of these datatypes
Jon Hall4ba53f02015-07-29 13:07:41 -070044
adminbae64d82013-08-01 10:50:15 -070045 Adding additional checks is done through coding simple functions.
Jon Hall4ba53f02015-07-29 13:07:41 -070046
47 The full set of standard checks are :
48
adminbae64d82013-08-01 10:50:15 -070049 * 'integer': matches integer values (including negative)
50 Takes optional 'min' and 'max' arguments : ::
Jon Hall4ba53f02015-07-29 13:07:41 -070051
adminbae64d82013-08-01 10:50:15 -070052 integer()
53 integer(3, 9) # any value from 3 to 9
54 integer(min=0) # any positive value
55 integer(max=9)
Jon Hall4ba53f02015-07-29 13:07:41 -070056
adminbae64d82013-08-01 10:50:15 -070057 * 'float': matches float values
58 Has the same parameters as the integer check.
Jon Hall4ba53f02015-07-29 13:07:41 -070059
adminbae64d82013-08-01 10:50:15 -070060 * '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
Jon Hall4ba53f02015-07-29 13:07:41 -070065
adminbae64d82013-08-01 10:50:15 -070066 Any other value raises an error.
Jon Hall4ba53f02015-07-29 13:07:41 -070067
adminbae64d82013-08-01 10:50:15 -070068 * 'ip_addr': matches an Internet Protocol address, v.4, represented
69 by a dotted-quad string, i.e. '1.2.3.4'.
Jon Hall4ba53f02015-07-29 13:07:41 -070070
adminbae64d82013-08-01 10:50:15 -070071 * 'string': matches any string.
72 Takes optional keyword args 'min' and 'max'
73 to specify min and max lengths of the string.
Jon Hall4ba53f02015-07-29 13:07:41 -070074
adminbae64d82013-08-01 10:50:15 -070075 * '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.)
Jon Hall4ba53f02015-07-29 13:07:41 -070078
adminbae64d82013-08-01 10:50:15 -070079 * '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.)
Jon Hall4ba53f02015-07-29 13:07:41 -070082
adminbae64d82013-08-01 10:50:15 -070083 * 'int_list': Matches a list of integers.
84 Takes the same arguments as list.
Jon Hall4ba53f02015-07-29 13:07:41 -070085
adminbae64d82013-08-01 10:50:15 -070086 * 'float_list': Matches a list of floats.
87 Takes the same arguments as list.
Jon Hall4ba53f02015-07-29 13:07:41 -070088
adminbae64d82013-08-01 10:50:15 -070089 * 'bool_list': Matches a list of boolean values.
90 Takes the same arguments as list.
Jon Hall4ba53f02015-07-29 13:07:41 -070091
adminbae64d82013-08-01 10:50:15 -070092 * 'ip_addr_list': Matches a list of IP addresses.
93 Takes the same arguments as list.
Jon Hall4ba53f02015-07-29 13:07:41 -070094
adminbae64d82013-08-01 10:50:15 -070095 * 'string_list': Matches a list of strings.
96 Takes the same arguments as list.
Jon Hall4ba53f02015-07-29 13:07:41 -070097
98 * 'mixed_list': Matches a list with different types in
adminbae64d82013-08-01 10:50:15 -070099 specific positions. List size must match
100 the number of arguments.
Jon Hall4ba53f02015-07-29 13:07:41 -0700101
adminbae64d82013-08-01 10:50:15 -0700102 Each position can be one of :
103 'integer', 'float', 'ip_addr', 'string', 'boolean'
Jon Hall4ba53f02015-07-29 13:07:41 -0700104
adminbae64d82013-08-01 10:50:15 -0700105 So to specify a list with two strings followed
106 by two integers, you write the check as : ::
Jon Hall4ba53f02015-07-29 13:07:41 -0700107
adminbae64d82013-08-01 10:50:15 -0700108 mixed_list('string', 'string', 'integer', 'integer')
Jon Hall4ba53f02015-07-29 13:07:41 -0700109
adminbae64d82013-08-01 10:50:15 -0700110 * 'pass': This check matches everything ! It never fails
111 and the value is unchanged.
Jon Hall4ba53f02015-07-29 13:07:41 -0700112
adminbae64d82013-08-01 10:50:15 -0700113 It is also the default if no check is specified.
Jon Hall4ba53f02015-07-29 13:07:41 -0700114
adminbae64d82013-08-01 10:50:15 -0700115 * 'option': This check matches any from a list of options.
116 You specify this check with : ::
Jon Hall4ba53f02015-07-29 13:07:41 -0700117
adminbae64d82013-08-01 10:50:15 -0700118 option('option 1', 'option 2', 'option 3')
Jon Hall4ba53f02015-07-29 13:07:41 -0700119
adminbae64d82013-08-01 10:50:15 -0700120 You can supply a default value (returned if no value is supplied)
121 using the default keyword argument.
Jon Hall4ba53f02015-07-29 13:07:41 -0700122
adminbae64d82013-08-01 10:50:15 -0700123 You specify a list argument for default using a list constructor syntax in
124 the check : ::
Jon Hall4ba53f02015-07-29 13:07:41 -0700125
adminbae64d82013-08-01 10:50:15 -0700126 checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3'))
Jon Hall4ba53f02015-07-29 13:07:41 -0700127
adminbae64d82013-08-01 10:50:15 -0700128 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
167import 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
248try:
249 bool
250except NameError:
251 def bool(val):
252 """Simple boolean equivalent function. """
253 if val:
254 return 1
255 else:
256 return 0
257
258
259def dottedQuadToNum(ip):
260 """
261 Convert decimal dotted quad string to long integer
Jon Hall4ba53f02015-07-29 13:07:41 -0700262
adminbae64d82013-08-01 10:50:15 -0700263 >>> 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 """
Jon Hall4ba53f02015-07-29 13:07:41 -0700277
adminbae64d82013-08-01 10:50:15 -0700278 # import here to avoid it when ip_addr values are not used
279 import socket, struct
Jon Hall4ba53f02015-07-29 13:07:41 -0700280
adminbae64d82013-08-01 10:50:15 -0700281 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
293def numToDottedQuad(num):
294 """
295 Convert long int to dotted quad string
Jon Hall4ba53f02015-07-29 13:07:41 -0700296
adminbae64d82013-08-01 10:50:15 -0700297 >>> 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 """
Jon Hall4ba53f02015-07-29 13:07:41 -0700314
adminbae64d82013-08-01 10:50:15 -0700315 # import here to avoid it when ip_addr values are not used
316 import socket, struct
Jon Hall4ba53f02015-07-29 13:07:41 -0700317
adminbae64d82013-08-01 10:50:15 -0700318 # 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
328class ValidateError(Exception):
329 """
330 This error indicates that the check failed.
331 It can be the base class for more specific errors.
Jon Hall4ba53f02015-07-29 13:07:41 -0700332
adminbae64d82013-08-01 10:50:15 -0700333 Any check function that fails ought to raise this error.
334 (or a subclass)
Jon Hall4ba53f02015-07-29 13:07:41 -0700335
adminbae64d82013-08-01 10:50:15 -0700336 >>> raise ValidateError
337 Traceback (most recent call last):
338 ValidateError
339 """
340
341
342class VdtMissingValue(ValidateError):
343 """No value was supplied to a check that needed one."""
344
345
346class 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
358class 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
370class 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
382class VdtValueError(ValidateError):
383 """The value supplied was of the correct type, but was not an allowed value."""
Jon Hall4ba53f02015-07-29 13:07:41 -0700384
adminbae64d82013-08-01 10:50:15 -0700385 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
394class 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
406class 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
418class 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
432class 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
444class 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.
Jon Hall4ba53f02015-07-29 13:07:41 -0700448
adminbae64d82013-08-01 10:50:15 -0700449 This can also involve converting the value from a string into
450 the correct datatype.
Jon Hall4ba53f02015-07-29 13:07:41 -0700451
adminbae64d82013-08-01 10:50:15 -0700452 The ``check`` method takes an input string which configures which
453 check is to be used and applies that check to a supplied value.
Jon Hall4ba53f02015-07-29 13:07:41 -0700454
adminbae64d82013-08-01 10:50:15 -0700455 An example input string would be:
456 'int_range(param1, param2)'
Jon Hall4ba53f02015-07-29 13:07:41 -0700457
adminbae64d82013-08-01 10:50:15 -0700458 You would then provide something like:
Jon Hall4ba53f02015-07-29 13:07:41 -0700459
adminbae64d82013-08-01 10:50:15 -0700460 >>> 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
Jon Hall4ba53f02015-07-29 13:07:41 -0700482
adminbae64d82013-08-01 10:50:15 -0700483 >>> 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.
Jon Hall4ba53f02015-07-29 13:07:41 -0700490
adminbae64d82013-08-01 10:50:15 -0700491 New functions can be added with : ::
Jon Hall4ba53f02015-07-29 13:07:41 -0700492
493 >>> vtr2 = Validator()
adminbae64d82013-08-01 10:50:15 -0700494 >>> vtr2.functions['int_range'] = int_range_check
Jon Hall4ba53f02015-07-29 13:07:41 -0700495
496 Or by passing in a dictionary of functions when Validator
adminbae64d82013-08-01 10:50:15 -0700497 is instantiated.
Jon Hall4ba53f02015-07-29 13:07:41 -0700498
adminbae64d82013-08-01 10:50:15 -0700499 Your functions *can* use keyword arguments,
500 but the first argument should always be 'value'.
Jon Hall4ba53f02015-07-29 13:07:41 -0700501
adminbae64d82013-08-01 10:50:15 -0700502 If the function doesn't take additional arguments,
503 the parentheses are optional in the check.
504 It can be written with either of : ::
Jon Hall4ba53f02015-07-29 13:07:41 -0700505
adminbae64d82013-08-01 10:50:15 -0700506 keyword = function_name
507 keyword = function_name()
Jon Hall4ba53f02015-07-29 13:07:41 -0700508
adminbae64d82013-08-01 10:50:15 -0700509 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)
Jon Hall4ba53f02015-07-29 13:07:41 -0700568
adminbae64d82013-08-01 10:50:15 -0700569 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
Jon Hall4ba53f02015-07-29 13:07:41 -0700573
adminbae64d82013-08-01 10:50:15 -0700574 If the check fails, raises a ``ValidateError`` subclass.
Jon Hall4ba53f02015-07-29 13:07:41 -0700575
adminbae64d82013-08-01 10:50:15 -0700576 >>> 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.
Jon Hall4ba53f02015-07-29 13:07:41 -0700582
adminbae64d82013-08-01 10:50:15 -0700583 >>> vtor.check('string(default="")', '', missing=True)
584 ''
585 """
586 fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check)
Jon Hall4ba53f02015-07-29 13:07:41 -0700587
adminbae64d82013-08-01 10:50:15 -0700588 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)
Jon Hall4ba53f02015-07-29 13:07:41 -0700593
adminbae64d82013-08-01 10:50:15 -0700594 if value is None:
595 return None
Jon Hall4ba53f02015-07-29 13:07:41 -0700596
adminbae64d82013-08-01 10:50:15 -0700597 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
Jon Hall4ba53f02015-07-29 13:07:41 -0700621
622
adminbae64d82013-08-01 10:50:15 -0700623 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
Jon Hall4ba53f02015-07-29 13:07:41 -0700660
adminbae64d82013-08-01 10:50:15 -0700661 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
Jon Hall4ba53f02015-07-29 13:07:41 -0700692
adminbae64d82013-08-01 10:50:15 -0700693 >>> vtor.check('', 0)
694 0
695 >>> vtor.check('', '0')
696 '0'
697 """
698 return value
Jon Hall4ba53f02015-07-29 13:07:41 -0700699
700
adminbae64d82013-08-01 10:50:15 -0700701 def get_default_value(self, check):
702 """
703 Given a check, return the default value for the check
704 (converted to the right type).
Jon Hall4ba53f02015-07-29 13:07:41 -0700705
adminbae64d82013-08-01 10:50:15 -0700706 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
718def _is_num_param(names, values, to_float=False):
719 """
720 Return numbers from inputs or raise VdtParamError.
Jon Hall4ba53f02015-07-29 13:07:41 -0700721
adminbae64d82013-08-01 10:50:15 -0700722 Lets ``None`` pass through.
723 Pass in keyword argument ``to_float=True`` to
724 use float for the conversion rather than int.
Jon Hall4ba53f02015-07-29 13:07:41 -0700725
adminbae64d82013-08-01 10:50:15 -0700726 >>> _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
755def 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.
Jon Hall4ba53f02015-07-29 13:07:41 -0700760
adminbae64d82013-08-01 10:50:15 -0700761 If the value is a string, then the conversion is done - if possible.
762 Otherwise a VdtError is raised.
Jon Hall4ba53f02015-07-29 13:07:41 -0700763
adminbae64d82013-08-01 10:50:15 -0700764 >>> 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
811def 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.
Jon Hall4ba53f02015-07-29 13:07:41 -0700815
adminbae64d82013-08-01 10:50:15 -0700816 If the value is a string, then the conversion is done - if possible.
817 Otherwise a VdtError is raised.
Jon Hall4ba53f02015-07-29 13:07:41 -0700818
adminbae64d82013-08-01 10:50:15 -0700819 This can accept negative values.
Jon Hall4ba53f02015-07-29 13:07:41 -0700820
adminbae64d82013-08-01 10:50:15 -0700821 >>> vtor.check('float', '2')
822 2.0
Jon Hall4ba53f02015-07-29 13:07:41 -0700823
adminbae64d82013-08-01 10:50:15 -0700824 From now on we multiply the value to avoid comparing decimals
Jon Hall4ba53f02015-07-29 13:07:41 -0700825
adminbae64d82013-08-01 10:50:15 -0700826 >>> 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
863bool_dict = {
Jon Hall4ba53f02015-07-29 13:07:41 -0700864 True: True, 'on': True, '1': True, 'true': True, 'yes': True,
adminbae64d82013-08-01 10:50:15 -0700865 False: False, 'off': False, '0': False, 'false': False, 'no': False,
866}
867
868
869def is_boolean(value):
870 """
871 Check if the value represents a boolean.
Jon Hall4ba53f02015-07-29 13:07:41 -0700872
adminbae64d82013-08-01 10:50:15 -0700873 >>> 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.
Jon Hall4ba53f02015-07-29 13:07:41 -0700911
adminbae64d82013-08-01 10:50:15 -0700912 """
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
929def 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'.
Jon Hall4ba53f02015-07-29 13:07:41 -0700933
adminbae64d82013-08-01 10:50:15 -0700934 >>> 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
966def is_list(value, min=None, max=None):
967 """
968 Check that the value is a list of values.
Jon Hall4ba53f02015-07-29 13:07:41 -0700969
adminbae64d82013-08-01 10:50:15 -0700970 You can optionally specify the minimum and maximum number of members.
Jon Hall4ba53f02015-07-29 13:07:41 -0700971
adminbae64d82013-08-01 10:50:15 -0700972 It does no check on list members.
Jon Hall4ba53f02015-07-29 13:07:41 -0700973
adminbae64d82013-08-01 10:50:15 -0700974 >>> 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
1011def is_tuple(value, min=None, max=None):
1012 """
1013 Check that the value is a tuple of values.
Jon Hall4ba53f02015-07-29 13:07:41 -07001014
adminbae64d82013-08-01 10:50:15 -07001015 You can optionally specify the minimum and maximum number of members.
Jon Hall4ba53f02015-07-29 13:07:41 -07001016
adminbae64d82013-08-01 10:50:15 -07001017 It does no check on members.
Jon Hall4ba53f02015-07-29 13:07:41 -07001018
adminbae64d82013-08-01 10:50:15 -07001019 >>> 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
1045def is_string(value, min=None, max=None):
1046 """
1047 Check that the supplied value is a string.
Jon Hall4ba53f02015-07-29 13:07:41 -07001048
adminbae64d82013-08-01 10:50:15 -07001049 You can optionally specify the minimum and maximum number of members.
Jon Hall4ba53f02015-07-29 13:07:41 -07001050
adminbae64d82013-08-01 10:50:15 -07001051 >>> 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
1081def is_int_list(value, min=None, max=None):
1082 """
1083 Check that the value is a list of integers.
Jon Hall4ba53f02015-07-29 13:07:41 -07001084
adminbae64d82013-08-01 10:50:15 -07001085 You can optionally specify the minimum and maximum number of members.
Jon Hall4ba53f02015-07-29 13:07:41 -07001086
adminbae64d82013-08-01 10:50:15 -07001087 Each list member is checked that it is an integer.
Jon Hall4ba53f02015-07-29 13:07:41 -07001088
adminbae64d82013-08-01 10:50:15 -07001089 >>> 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
1104def is_bool_list(value, min=None, max=None):
1105 """
1106 Check that the value is a list of booleans.
Jon Hall4ba53f02015-07-29 13:07:41 -07001107
adminbae64d82013-08-01 10:50:15 -07001108 You can optionally specify the minimum and maximum number of members.
Jon Hall4ba53f02015-07-29 13:07:41 -07001109
adminbae64d82013-08-01 10:50:15 -07001110 Each list member is checked that it is a boolean.
Jon Hall4ba53f02015-07-29 13:07:41 -07001111
adminbae64d82013-08-01 10:50:15 -07001112 >>> 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
1129def is_float_list(value, min=None, max=None):
1130 """
1131 Check that the value is a list of floats.
Jon Hall4ba53f02015-07-29 13:07:41 -07001132
adminbae64d82013-08-01 10:50:15 -07001133 You can optionally specify the minimum and maximum number of members.
Jon Hall4ba53f02015-07-29 13:07:41 -07001134
adminbae64d82013-08-01 10:50:15 -07001135 Each list member is checked that it is a float.
Jon Hall4ba53f02015-07-29 13:07:41 -07001136
adminbae64d82013-08-01 10:50:15 -07001137 >>> 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
1152def is_string_list(value, min=None, max=None):
1153 """
1154 Check that the value is a list of strings.
Jon Hall4ba53f02015-07-29 13:07:41 -07001155
adminbae64d82013-08-01 10:50:15 -07001156 You can optionally specify the minimum and maximum number of members.
Jon Hall4ba53f02015-07-29 13:07:41 -07001157
adminbae64d82013-08-01 10:50:15 -07001158 Each list member is checked that it is a string.
Jon Hall4ba53f02015-07-29 13:07:41 -07001159
adminbae64d82013-08-01 10:50:15 -07001160 >>> 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
1178def is_ip_addr_list(value, min=None, max=None):
1179 """
1180 Check that the value is a list of IP addresses.
Jon Hall4ba53f02015-07-29 13:07:41 -07001181
adminbae64d82013-08-01 10:50:15 -07001182 You can optionally specify the minimum and maximum number of members.
Jon Hall4ba53f02015-07-29 13:07:41 -07001183
adminbae64d82013-08-01 10:50:15 -07001184 Each list member is checked that it is an IP address.
Jon Hall4ba53f02015-07-29 13:07:41 -07001185
adminbae64d82013-08-01 10:50:15 -07001186 >>> 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
1199def 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.
Jon Hall4ba53f02015-07-29 13:07:41 -07001204
adminbae64d82013-08-01 10:50:15 -07001205 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.
Jon Hall4ba53f02015-07-29 13:07:41 -07001208
adminbae64d82013-08-01 10:50:15 -07001209 >>> 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)
Jon Hall4ba53f02015-07-29 13:07:41 -07001219
1220
adminbae64d82013-08-01 10:50:15 -07001221
1222fun_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
1231def 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.
Jon Hall4ba53f02015-07-29 13:07:41 -07001236
adminbae64d82013-08-01 10:50:15 -07001237 You specify each member as a positional argument specifying type
Jon Hall4ba53f02015-07-29 13:07:41 -07001238
adminbae64d82013-08-01 10:50:15 -07001239 Each type should be one of the following strings :
1240 'integer', 'float', 'ip_addr', 'string', 'boolean'
Jon Hall4ba53f02015-07-29 13:07:41 -07001241
adminbae64d82013-08-01 10:50:15 -07001242 So you can specify a list of two strings, followed by
1243 two integers as :
Jon Hall4ba53f02015-07-29 13:07:41 -07001244
adminbae64d82013-08-01 10:50:15 -07001245 mixed_list('string', 'string', 'integer', 'integer')
Jon Hall4ba53f02015-07-29 13:07:41 -07001246
adminbae64d82013-08-01 10:50:15 -07001247 The length of the list must match the number of positional
1248 arguments you supply.
Jon Hall4ba53f02015-07-29 13:07:41 -07001249
adminbae64d82013-08-01 10:50:15 -07001250 >>> 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.
Jon Hall4ba53f02015-07-29 13:07:41 -07001269
adminbae64d82013-08-01 10:50:15 -07001270 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 .
Jon Hall4ba53f02015-07-29 13:07:41 -07001272
adminbae64d82013-08-01 10:50:15 -07001273 >>> 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
1299def is_option(value, *options):
1300 """
1301 This check matches the value to any of a set of options.
Jon Hall4ba53f02015-07-29 13:07:41 -07001302
adminbae64d82013-08-01 10:50:15 -07001303 >>> 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
1319def _test(value, *args, **keywargs):
1320 """
1321 A function that exists for test purposes.
Jon Hall4ba53f02015-07-29 13:07:41 -07001322
adminbae64d82013-08-01 10:50:15 -07001323 >>> 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)'})
Jon Hall4ba53f02015-07-29 13:07:41 -07001356
adminbae64d82013-08-01 10:50:15 -07001357 >>> 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']
Jon Hall4ba53f02015-07-29 13:07:41 -07001374
adminbae64d82013-08-01 10:50:15 -07001375 >>> 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']
Jon Hall4ba53f02015-07-29 13:07:41 -07001383
adminbae64d82013-08-01 10:50:15 -07001384 Bug test for unicode arguments
1385 >>> v = Validator()
1386 >>> v.check(u'string(min=4)', u'test')
1387 u'test'
Jon Hall4ba53f02015-07-29 13:07:41 -07001388
adminbae64d82013-08-01 10:50:15 -07001389 >>> 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'
Jon Hall4ba53f02015-07-29 13:07:41 -07001394
adminbae64d82013-08-01 10:50:15 -07001395 >>> v = Validator()
1396 >>> default = v.get_default_value('string(default=None)')
1397 >>> default == None
1398 1
1399 """
1400 return (value, args, keywargs)
1401
1402
1403def _test2():
1404 """
Jon Hall4ba53f02015-07-29 13:07:41 -07001405 >>>
adminbae64d82013-08-01 10:50:15 -07001406 >>> 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
1413def _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 """
Jon Hall4ba53f02015-07-29 13:07:41 -07001439
1440
adminbae64d82013-08-01 10:50:15 -07001441if __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)