Package pythonutils :: Module validate
[hide private]
[frames] | no frames]

Source Code for Module pythonutils.validate

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