12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451 |
- # validate.py
- # A Validator object
- # Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa
- # E-mail: fuzzyman AT voidspace DOT org DOT uk
- # mark AT la-la DOT com
- # nico AT tekNico DOT net
- # This software is licensed under the terms of the BSD license.
- # http://www.voidspace.org.uk/python/license.shtml
- # Basically you're free to copy, modify, distribute and relicense it,
- # So long as you keep a copy of the license with it.
- # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
- # For information about bugfixes, updates and support, please join the
- # ConfigObj mailing list:
- # http://lists.sourceforge.net/lists/listinfo/configobj-develop
- # Comments, suggestions and bug reports welcome.
- """
- The Validator object is used to check that supplied values
- conform to a specification.
-
- The value can be supplied as a string - e.g. from a config file.
- In this case the check will also *convert* the value to
- the required type. This allows you to add validation
- as a transparent layer to access data stored as strings.
- The validation checks that the data is correct *and*
- converts it to the expected type.
-
- Some standard checks are provided for basic data types.
- Additional checks are easy to write. They can be
- provided when the ``Validator`` is instantiated or
- added afterwards.
-
- The standard functions work with the following basic data types :
-
- * integers
- * floats
- * booleans
- * strings
- * ip_addr
-
- plus lists of these datatypes
-
- Adding additional checks is done through coding simple functions.
-
- The full set of standard checks are :
-
- * 'integer': matches integer values (including negative)
- Takes optional 'min' and 'max' arguments : ::
-
- integer()
- integer(3, 9) # any value from 3 to 9
- integer(min=0) # any positive value
- integer(max=9)
-
- * 'float': matches float values
- Has the same parameters as the integer check.
-
- * 'boolean': matches boolean values - ``True`` or ``False``
- Acceptable string values for True are :
- true, on, yes, 1
- Acceptable string values for False are :
- false, off, no, 0
-
- Any other value raises an error.
-
- * 'ip_addr': matches an Internet Protocol address, v.4, represented
- by a dotted-quad string, i.e. '1.2.3.4'.
-
- * 'string': matches any string.
- Takes optional keyword args 'min' and 'max'
- to specify min and max lengths of the string.
-
- * 'list': matches any list.
- Takes optional keyword args 'min', and 'max' to specify min and
- max sizes of the list. (Always returns a list.)
-
- * 'tuple': matches any tuple.
- Takes optional keyword args 'min', and 'max' to specify min and
- max sizes of the tuple. (Always returns a tuple.)
-
- * 'int_list': Matches a list of integers.
- Takes the same arguments as list.
-
- * 'float_list': Matches a list of floats.
- Takes the same arguments as list.
-
- * 'bool_list': Matches a list of boolean values.
- Takes the same arguments as list.
-
- * 'ip_addr_list': Matches a list of IP addresses.
- Takes the same arguments as list.
-
- * 'string_list': Matches a list of strings.
- Takes the same arguments as list.
-
- * 'mixed_list': Matches a list with different types in
- specific positions. List size must match
- the number of arguments.
-
- Each position can be one of :
- 'integer', 'float', 'ip_addr', 'string', 'boolean'
-
- So to specify a list with two strings followed
- by two integers, you write the check as : ::
-
- mixed_list('string', 'string', 'integer', 'integer')
-
- * 'pass': This check matches everything ! It never fails
- and the value is unchanged.
-
- It is also the default if no check is specified.
-
- * 'option': This check matches any from a list of options.
- You specify this check with : ::
-
- option('option 1', 'option 2', 'option 3')
-
- You can supply a default value (returned if no value is supplied)
- using the default keyword argument.
-
- You specify a list argument for default using a list constructor syntax in
- the check : ::
-
- checkname(arg1, arg2, default=list('val 1', 'val 2', 'val 3'))
-
- A badly formatted set of arguments will raise a ``VdtParamError``.
- """
- __version__ = '1.0.1'
- __all__ = (
- '__version__',
- 'dottedQuadToNum',
- 'numToDottedQuad',
- 'ValidateError',
- 'VdtUnknownCheckError',
- 'VdtParamError',
- 'VdtTypeError',
- 'VdtValueError',
- 'VdtValueTooSmallError',
- 'VdtValueTooBigError',
- 'VdtValueTooShortError',
- 'VdtValueTooLongError',
- 'VdtMissingValue',
- 'Validator',
- 'is_integer',
- 'is_float',
- 'is_boolean',
- 'is_list',
- 'is_tuple',
- 'is_ip_addr',
- 'is_string',
- 'is_int_list',
- 'is_bool_list',
- 'is_float_list',
- 'is_string_list',
- 'is_ip_addr_list',
- 'is_mixed_list',
- 'is_option',
- '__docformat__',
- )
- import re
- _list_arg = re.compile(r'''
- (?:
- ([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*list\(
- (
- (?:
- \s*
- (?:
- (?:".*?")| # double quotes
- (?:'.*?')| # single quotes
- (?:[^'",\s\)][^,\)]*?) # unquoted
- )
- \s*,\s*
- )*
- (?:
- (?:".*?")| # double quotes
- (?:'.*?')| # single quotes
- (?:[^'",\s\)][^,\)]*?) # unquoted
- )? # last one
- )
- \)
- )
- ''', re.VERBOSE | re.DOTALL) # two groups
- _list_members = re.compile(r'''
- (
- (?:".*?")| # double quotes
- (?:'.*?')| # single quotes
- (?:[^'",\s=][^,=]*?) # unquoted
- )
- (?:
- (?:\s*,\s*)|(?:\s*$) # comma
- )
- ''', re.VERBOSE | re.DOTALL) # one group
- _paramstring = r'''
- (?:
- (
- (?:
- [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*list\(
- (?:
- \s*
- (?:
- (?:".*?")| # double quotes
- (?:'.*?')| # single quotes
- (?:[^'",\s\)][^,\)]*?) # unquoted
- )
- \s*,\s*
- )*
- (?:
- (?:".*?")| # double quotes
- (?:'.*?')| # single quotes
- (?:[^'",\s\)][^,\)]*?) # unquoted
- )? # last one
- \)
- )|
- (?:
- (?:".*?")| # double quotes
- (?:'.*?')| # single quotes
- (?:[^'",\s=][^,=]*?)| # unquoted
- (?: # keyword argument
- [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*
- (?:
- (?:".*?")| # double quotes
- (?:'.*?')| # single quotes
- (?:[^'",\s=][^,=]*?) # unquoted
- )
- )
- )
- )
- (?:
- (?:\s*,\s*)|(?:\s*$) # comma
- )
- )
- '''
- _matchstring = '^%s*' % _paramstring
- # Python pre 2.2.1 doesn't have bool
- try:
- bool
- except NameError:
- def bool(val):
- """Simple boolean equivalent function. """
- if val:
- return 1
- else:
- return 0
- def dottedQuadToNum(ip):
- """
- Convert decimal dotted quad string to long integer
-
- >>> int(dottedQuadToNum('1 '))
- 1
- >>> int(dottedQuadToNum(' 1.2'))
- 16777218
- >>> int(dottedQuadToNum(' 1.2.3 '))
- 16908291
- >>> int(dottedQuadToNum('1.2.3.4'))
- 16909060
- >>> dottedQuadToNum('255.255.255.255')
- 4294967295L
- >>> dottedQuadToNum('255.255.255.256')
- Traceback (most recent call last):
- ValueError: Not a good dotted-quad IP: 255.255.255.256
- """
-
- # import here to avoid it when ip_addr values are not used
- import socket, struct
-
- try:
- return struct.unpack('!L',
- socket.inet_aton(ip.strip()))[0]
- except socket.error:
- # bug in inet_aton, corrected in Python 2.4
- if ip.strip() == '255.255.255.255':
- return 0xFFFFFFFFL
- else:
- raise ValueError('Not a good dotted-quad IP: %s' % ip)
- return
- def numToDottedQuad(num):
- """
- Convert long int to dotted quad string
-
- >>> numToDottedQuad(-1L)
- Traceback (most recent call last):
- ValueError: Not a good numeric IP: -1
- >>> numToDottedQuad(1L)
- '0.0.0.1'
- >>> numToDottedQuad(16777218L)
- '1.0.0.2'
- >>> numToDottedQuad(16908291L)
- '1.2.0.3'
- >>> numToDottedQuad(16909060L)
- '1.2.3.4'
- >>> numToDottedQuad(4294967295L)
- '255.255.255.255'
- >>> numToDottedQuad(4294967296L)
- Traceback (most recent call last):
- ValueError: Not a good numeric IP: 4294967296
- """
-
- # import here to avoid it when ip_addr values are not used
- import socket, struct
-
- # no need to intercept here, 4294967295L is fine
- if num > 4294967295L or num < 0:
- raise ValueError('Not a good numeric IP: %s' % num)
- try:
- return socket.inet_ntoa(
- struct.pack('!L', long(num)))
- except (socket.error, struct.error, OverflowError):
- raise ValueError('Not a good numeric IP: %s' % num)
- class ValidateError(Exception):
- """
- This error indicates that the check failed.
- It can be the base class for more specific errors.
-
- Any check function that fails ought to raise this error.
- (or a subclass)
-
- >>> raise ValidateError
- Traceback (most recent call last):
- ValidateError
- """
- class VdtMissingValue(ValidateError):
- """No value was supplied to a check that needed one."""
- class VdtUnknownCheckError(ValidateError):
- """An unknown check function was requested"""
- def __init__(self, value):
- """
- >>> raise VdtUnknownCheckError('yoda')
- Traceback (most recent call last):
- VdtUnknownCheckError: the check "yoda" is unknown.
- """
- ValidateError.__init__(self, 'the check "%s" is unknown.' % (value,))
- class VdtParamError(SyntaxError):
- """An incorrect parameter was passed"""
- def __init__(self, name, value):
- """
- >>> raise VdtParamError('yoda', 'jedi')
- Traceback (most recent call last):
- VdtParamError: passed an incorrect value "jedi" for parameter "yoda".
- """
- SyntaxError.__init__(self, 'passed an incorrect value "%s" for parameter "%s".' % (value, name))
- class VdtTypeError(ValidateError):
- """The value supplied was of the wrong type"""
- def __init__(self, value):
- """
- >>> raise VdtTypeError('jedi')
- Traceback (most recent call last):
- VdtTypeError: the value "jedi" is of the wrong type.
- """
- ValidateError.__init__(self, 'the value "%s" is of the wrong type.' % (value,))
- class VdtValueError(ValidateError):
- """The value supplied was of the correct type, but was not an allowed value."""
-
- def __init__(self, value):
- """
- >>> raise VdtValueError('jedi')
- Traceback (most recent call last):
- VdtValueError: the value "jedi" is unacceptable.
- """
- ValidateError.__init__(self, 'the value "%s" is unacceptable.' % (value,))
- class VdtValueTooSmallError(VdtValueError):
- """The value supplied was of the correct type, but was too small."""
- def __init__(self, value):
- """
- >>> raise VdtValueTooSmallError('0')
- Traceback (most recent call last):
- VdtValueTooSmallError: the value "0" is too small.
- """
- ValidateError.__init__(self, 'the value "%s" is too small.' % (value,))
- class VdtValueTooBigError(VdtValueError):
- """The value supplied was of the correct type, but was too big."""
- def __init__(self, value):
- """
- >>> raise VdtValueTooBigError('1')
- Traceback (most recent call last):
- VdtValueTooBigError: the value "1" is too big.
- """
- ValidateError.__init__(self, 'the value "%s" is too big.' % (value,))
- class VdtValueTooShortError(VdtValueError):
- """The value supplied was of the correct type, but was too short."""
- def __init__(self, value):
- """
- >>> raise VdtValueTooShortError('jed')
- Traceback (most recent call last):
- VdtValueTooShortError: the value "jed" is too short.
- """
- ValidateError.__init__(
- self,
- 'the value "%s" is too short.' % (value,))
- class VdtValueTooLongError(VdtValueError):
- """The value supplied was of the correct type, but was too long."""
- def __init__(self, value):
- """
- >>> raise VdtValueTooLongError('jedie')
- Traceback (most recent call last):
- VdtValueTooLongError: the value "jedie" is too long.
- """
- ValidateError.__init__(self, 'the value "%s" is too long.' % (value,))
- class Validator(object):
- """
- Validator is an object that allows you to register a set of 'checks'.
- These checks take input and test that it conforms to the check.
-
- This can also involve converting the value from a string into
- the correct datatype.
-
- The ``check`` method takes an input string which configures which
- check is to be used and applies that check to a supplied value.
-
- An example input string would be:
- 'int_range(param1, param2)'
-
- You would then provide something like:
-
- >>> def int_range_check(value, min, max):
- ... # turn min and max from strings to integers
- ... min = int(min)
- ... max = int(max)
- ... # check that value is of the correct type.
- ... # possible valid inputs are integers or strings
- ... # that represent integers
- ... if not isinstance(value, (int, long, basestring)):
- ... raise VdtTypeError(value)
- ... elif isinstance(value, basestring):
- ... # if we are given a string
- ... # attempt to convert to an integer
- ... try:
- ... value = int(value)
- ... except ValueError:
- ... raise VdtValueError(value)
- ... # check the value is between our constraints
- ... if not min <= value:
- ... raise VdtValueTooSmallError(value)
- ... if not value <= max:
- ... raise VdtValueTooBigError(value)
- ... return value
-
- >>> fdict = {'int_range': int_range_check}
- >>> vtr1 = Validator(fdict)
- >>> vtr1.check('int_range(20, 40)', '30')
- 30
- >>> vtr1.check('int_range(20, 40)', '60')
- Traceback (most recent call last):
- VdtValueTooBigError: the value "60" is too big.
-
- New functions can be added with : ::
-
- >>> vtr2 = Validator()
- >>> vtr2.functions['int_range'] = int_range_check
-
- Or by passing in a dictionary of functions when Validator
- is instantiated.
-
- Your functions *can* use keyword arguments,
- but the first argument should always be 'value'.
-
- If the function doesn't take additional arguments,
- the parentheses are optional in the check.
- It can be written with either of : ::
-
- keyword = function_name
- keyword = function_name()
-
- The first program to utilise Validator() was Michael Foord's
- ConfigObj, an alternative to ConfigParser which supports lists and
- can validate a config file using a config schema.
- For more details on using Validator with ConfigObj see:
- http://www.voidspace.org.uk/python/configobj.html
- """
- # this regex does the initial parsing of the checks
- _func_re = re.compile(r'(.+?)\((.*)\)', re.DOTALL)
- # this regex takes apart keyword arguments
- _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$', re.DOTALL)
- # this regex finds keyword=list(....) type values
- _list_arg = _list_arg
- # this regex takes individual values out of lists - in one pass
- _list_members = _list_members
- # These regexes check a set of arguments for validity
- # and then pull the members out
- _paramfinder = re.compile(_paramstring, re.VERBOSE | re.DOTALL)
- _matchfinder = re.compile(_matchstring, re.VERBOSE | re.DOTALL)
- def __init__(self, functions=None):
- """
- >>> vtri = Validator()
- """
- self.functions = {
- '': self._pass,
- 'integer': is_integer,
- 'float': is_float,
- 'boolean': is_boolean,
- 'ip_addr': is_ip_addr,
- 'string': is_string,
- 'list': is_list,
- 'tuple': is_tuple,
- 'int_list': is_int_list,
- 'float_list': is_float_list,
- 'bool_list': is_bool_list,
- 'ip_addr_list': is_ip_addr_list,
- 'string_list': is_string_list,
- 'mixed_list': is_mixed_list,
- 'pass': self._pass,
- 'option': is_option,
- 'force_list': force_list,
- }
- if functions is not None:
- self.functions.update(functions)
- # tekNico: for use by ConfigObj
- self.baseErrorClass = ValidateError
- self._cache = {}
- def check(self, check, value, missing=False):
- """
- Usage: check(check, value)
-
- Arguments:
- check: string representing check to apply (including arguments)
- value: object to be checked
- Returns value, converted to correct type if necessary
-
- If the check fails, raises a ``ValidateError`` subclass.
-
- >>> vtor.check('yoda', '')
- Traceback (most recent call last):
- VdtUnknownCheckError: the check "yoda" is unknown.
- >>> vtor.check('yoda()', '')
- Traceback (most recent call last):
- VdtUnknownCheckError: the check "yoda" is unknown.
-
- >>> vtor.check('string(default="")', '', missing=True)
- ''
- """
- fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check)
-
- if missing:
- if default is None:
- # no information needed here - to be handled by caller
- raise VdtMissingValue()
- value = self._handle_none(default)
-
- if value is None:
- return None
-
- return self._check_value(value, fun_name, fun_args, fun_kwargs)
- def _handle_none(self, value):
- if value == 'None':
- return None
- elif value in ("'None'", '"None"'):
- # Special case a quoted None
- value = self._unquote(value)
- return value
- def _parse_with_caching(self, check):
- if check in self._cache:
- fun_name, fun_args, fun_kwargs, default = self._cache[check]
- # We call list and dict below to work with *copies* of the data
- # rather than the original (which are mutable of course)
- fun_args = list(fun_args)
- fun_kwargs = dict(fun_kwargs)
- else:
- fun_name, fun_args, fun_kwargs, default = self._parse_check(check)
- fun_kwargs = dict([(str(key), value) for (key, value) in fun_kwargs.items()])
- self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default
- return fun_name, fun_args, fun_kwargs, default
-
-
- def _check_value(self, value, fun_name, fun_args, fun_kwargs):
- try:
- fun = self.functions[fun_name]
- except KeyError:
- raise VdtUnknownCheckError(fun_name)
- else:
- return fun(value, *fun_args, **fun_kwargs)
- def _parse_check(self, check):
- fun_match = self._func_re.match(check)
- if fun_match:
- fun_name = fun_match.group(1)
- arg_string = fun_match.group(2)
- arg_match = self._matchfinder.match(arg_string)
- if arg_match is None:
- # Bad syntax
- raise VdtParamError('Bad syntax in check "%s".' % check)
- fun_args = []
- fun_kwargs = {}
- # pull out args of group 2
- for arg in self._paramfinder.findall(arg_string):
- # args may need whitespace removing (before removing quotes)
- arg = arg.strip()
- listmatch = self._list_arg.match(arg)
- if listmatch:
- key, val = self._list_handle(listmatch)
- fun_kwargs[key] = val
- continue
- keymatch = self._key_arg.match(arg)
- if keymatch:
- val = keymatch.group(2)
- if not val in ("'None'", '"None"'):
- # Special case a quoted None
- val = self._unquote(val)
- fun_kwargs[keymatch.group(1)] = val
- continue
-
- fun_args.append(self._unquote(arg))
- else:
- # allows for function names without (args)
- return check, (), {}, None
- # Default must be deleted if the value is specified too,
- # otherwise the check function will get a spurious "default" keyword arg
- default = fun_kwargs.pop('default', None)
- return fun_name, fun_args, fun_kwargs, default
- def _unquote(self, val):
- """Unquote a value if necessary."""
- if (len(val) >= 2) and (val[0] in ("'", '"')) and (val[0] == val[-1]):
- val = val[1:-1]
- return val
- def _list_handle(self, listmatch):
- """Take apart a ``keyword=list('val, 'val')`` type string."""
- out = []
- name = listmatch.group(1)
- args = listmatch.group(2)
- for arg in self._list_members.findall(args):
- out.append(self._unquote(arg))
- return name, out
- def _pass(self, value):
- """
- Dummy check that always passes
-
- >>> vtor.check('', 0)
- 0
- >>> vtor.check('', '0')
- '0'
- """
- return value
-
-
- def get_default_value(self, check):
- """
- Given a check, return the default value for the check
- (converted to the right type).
-
- If the check doesn't specify a default value then a
- ``KeyError`` will be raised.
- """
- fun_name, fun_args, fun_kwargs, default = self._parse_with_caching(check)
- if default is None:
- raise KeyError('Check "%s" has no default value.' % check)
- value = self._handle_none(default)
- if value is None:
- return value
- return self._check_value(value, fun_name, fun_args, fun_kwargs)
- def _is_num_param(names, values, to_float=False):
- """
- Return numbers from inputs or raise VdtParamError.
-
- Lets ``None`` pass through.
- Pass in keyword argument ``to_float=True`` to
- use float for the conversion rather than int.
-
- >>> _is_num_param(('', ''), (0, 1.0))
- [0, 1]
- >>> _is_num_param(('', ''), (0, 1.0), to_float=True)
- [0.0, 1.0]
- >>> _is_num_param(('a'), ('a'))
- Traceback (most recent call last):
- VdtParamError: passed an incorrect value "a" for parameter "a".
- """
- fun = to_float and float or int
- out_params = []
- for (name, val) in zip(names, values):
- if val is None:
- out_params.append(val)
- elif isinstance(val, (int, long, float, basestring)):
- try:
- out_params.append(fun(val))
- except ValueError, e:
- raise VdtParamError(name, val)
- else:
- raise VdtParamError(name, val)
- return out_params
- # built in checks
- # you can override these by setting the appropriate name
- # in Validator.functions
- # note: if the params are specified wrongly in your input string,
- # you will also raise errors.
- def is_integer(value, min=None, max=None):
- """
- A check that tests that a given value is an integer (int, or long)
- and optionally, between bounds. A negative value is accepted, while
- a float will fail.
-
- If the value is a string, then the conversion is done - if possible.
- Otherwise a VdtError is raised.
-
- >>> vtor.check('integer', '-1')
- -1
- >>> vtor.check('integer', '0')
- 0
- >>> vtor.check('integer', 9)
- 9
- >>> vtor.check('integer', 'a')
- Traceback (most recent call last):
- VdtTypeError: the value "a" is of the wrong type.
- >>> vtor.check('integer', '2.2')
- Traceback (most recent call last):
- VdtTypeError: the value "2.2" is of the wrong type.
- >>> vtor.check('integer(10)', '20')
- 20
- >>> vtor.check('integer(max=20)', '15')
- 15
- >>> vtor.check('integer(10)', '9')
- Traceback (most recent call last):
- VdtValueTooSmallError: the value "9" is too small.
- >>> vtor.check('integer(10)', 9)
- Traceback (most recent call last):
- VdtValueTooSmallError: the value "9" is too small.
- >>> vtor.check('integer(max=20)', '35')
- Traceback (most recent call last):
- VdtValueTooBigError: the value "35" is too big.
- >>> vtor.check('integer(max=20)', 35)
- Traceback (most recent call last):
- VdtValueTooBigError: the value "35" is too big.
- >>> vtor.check('integer(0, 9)', False)
- 0
- """
- (min_val, max_val) = _is_num_param(('min', 'max'), (min, max))
- if not isinstance(value, (int, long, basestring)):
- raise VdtTypeError(value)
- if isinstance(value, basestring):
- # if it's a string - does it represent an integer ?
- try:
- value = int(value)
- except ValueError:
- raise VdtTypeError(value)
- if (min_val is not None) and (value < min_val):
- raise VdtValueTooSmallError(value)
- if (max_val is not None) and (value > max_val):
- raise VdtValueTooBigError(value)
- return value
- def is_float(value, min=None, max=None):
- """
- A check that tests that a given value is a float
- (an integer will be accepted), and optionally - that it is between bounds.
-
- If the value is a string, then the conversion is done - if possible.
- Otherwise a VdtError is raised.
-
- This can accept negative values.
-
- >>> vtor.check('float', '2')
- 2.0
-
- From now on we multiply the value to avoid comparing decimals
-
- >>> vtor.check('float', '-6.8') * 10
- -68.0
- >>> vtor.check('float', '12.2') * 10
- 122.0
- >>> vtor.check('float', 8.4) * 10
- 84.0
- >>> vtor.check('float', 'a')
- Traceback (most recent call last):
- VdtTypeError: the value "a" is of the wrong type.
- >>> vtor.check('float(10.1)', '10.2') * 10
- 102.0
- >>> vtor.check('float(max=20.2)', '15.1') * 10
- 151.0
- >>> vtor.check('float(10.0)', '9.0')
- Traceback (most recent call last):
- VdtValueTooSmallError: the value "9.0" is too small.
- >>> vtor.check('float(max=20.0)', '35.0')
- Traceback (most recent call last):
- VdtValueTooBigError: the value "35.0" is too big.
- """
- (min_val, max_val) = _is_num_param(
- ('min', 'max'), (min, max), to_float=True)
- if not isinstance(value, (int, long, float, basestring)):
- raise VdtTypeError(value)
- if not isinstance(value, float):
- # if it's a string - does it represent a float ?
- try:
- value = float(value)
- except ValueError:
- raise VdtTypeError(value)
- if (min_val is not None) and (value < min_val):
- raise VdtValueTooSmallError(value)
- if (max_val is not None) and (value > max_val):
- raise VdtValueTooBigError(value)
- return value
- bool_dict = {
- True: True, 'on': True, '1': True, 'true': True, 'yes': True,
- False: False, 'off': False, '0': False, 'false': False, 'no': False,
- }
- def is_boolean(value):
- """
- Check if the value represents a boolean.
-
- >>> vtor.check('boolean', 0)
- 0
- >>> vtor.check('boolean', False)
- 0
- >>> vtor.check('boolean', '0')
- 0
- >>> vtor.check('boolean', 'off')
- 0
- >>> vtor.check('boolean', 'false')
- 0
- >>> vtor.check('boolean', 'no')
- 0
- >>> vtor.check('boolean', 'nO')
- 0
- >>> vtor.check('boolean', 'NO')
- 0
- >>> vtor.check('boolean', 1)
- 1
- >>> vtor.check('boolean', True)
- 1
- >>> vtor.check('boolean', '1')
- 1
- >>> vtor.check('boolean', 'on')
- 1
- >>> vtor.check('boolean', 'true')
- 1
- >>> vtor.check('boolean', 'yes')
- 1
- >>> vtor.check('boolean', 'Yes')
- 1
- >>> vtor.check('boolean', 'YES')
- 1
- >>> vtor.check('boolean', '')
- Traceback (most recent call last):
- VdtTypeError: the value "" is of the wrong type.
- >>> vtor.check('boolean', 'up')
- Traceback (most recent call last):
- VdtTypeError: the value "up" is of the wrong type.
-
- """
- if isinstance(value, basestring):
- try:
- return bool_dict[value.lower()]
- except KeyError:
- raise VdtTypeError(value)
- # we do an equality test rather than an identity test
- # this ensures Python 2.2 compatibilty
- # and allows 0 and 1 to represent True and False
- if value == False:
- return False
- elif value == True:
- return True
- else:
- raise VdtTypeError(value)
- def is_ip_addr(value):
- """
- Check that the supplied value is an Internet Protocol address, v.4,
- represented by a dotted-quad string, i.e. '1.2.3.4'.
-
- >>> vtor.check('ip_addr', '1 ')
- '1'
- >>> vtor.check('ip_addr', ' 1.2')
- '1.2'
- >>> vtor.check('ip_addr', ' 1.2.3 ')
- '1.2.3'
- >>> vtor.check('ip_addr', '1.2.3.4')
- '1.2.3.4'
- >>> vtor.check('ip_addr', '0.0.0.0')
- '0.0.0.0'
- >>> vtor.check('ip_addr', '255.255.255.255')
- '255.255.255.255'
- >>> vtor.check('ip_addr', '255.255.255.256')
- Traceback (most recent call last):
- VdtValueError: the value "255.255.255.256" is unacceptable.
- >>> vtor.check('ip_addr', '1.2.3.4.5')
- Traceback (most recent call last):
- VdtValueError: the value "1.2.3.4.5" is unacceptable.
- >>> vtor.check('ip_addr', 0)
- Traceback (most recent call last):
- VdtTypeError: the value "0" is of the wrong type.
- """
- if not isinstance(value, basestring):
- raise VdtTypeError(value)
- value = value.strip()
- try:
- dottedQuadToNum(value)
- except ValueError:
- raise VdtValueError(value)
- return value
- def is_list(value, min=None, max=None):
- """
- Check that the value is a list of values.
-
- You can optionally specify the minimum and maximum number of members.
-
- It does no check on list members.
-
- >>> vtor.check('list', ())
- []
- >>> vtor.check('list', [])
- []
- >>> vtor.check('list', (1, 2))
- [1, 2]
- >>> vtor.check('list', [1, 2])
- [1, 2]
- >>> vtor.check('list(3)', (1, 2))
- Traceback (most recent call last):
- VdtValueTooShortError: the value "(1, 2)" is too short.
- >>> vtor.check('list(max=5)', (1, 2, 3, 4, 5, 6))
- Traceback (most recent call last):
- VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long.
- >>> vtor.check('list(min=3, max=5)', (1, 2, 3, 4))
- [1, 2, 3, 4]
- >>> vtor.check('list', 0)
- Traceback (most recent call last):
- VdtTypeError: the value "0" is of the wrong type.
- >>> vtor.check('list', '12')
- Traceback (most recent call last):
- VdtTypeError: the value "12" is of the wrong type.
- """
- (min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
- if isinstance(value, basestring):
- raise VdtTypeError(value)
- try:
- num_members = len(value)
- except TypeError:
- raise VdtTypeError(value)
- if min_len is not None and num_members < min_len:
- raise VdtValueTooShortError(value)
- if max_len is not None and num_members > max_len:
- raise VdtValueTooLongError(value)
- return list(value)
- def is_tuple(value, min=None, max=None):
- """
- Check that the value is a tuple of values.
-
- You can optionally specify the minimum and maximum number of members.
-
- It does no check on members.
-
- >>> vtor.check('tuple', ())
- ()
- >>> vtor.check('tuple', [])
- ()
- >>> vtor.check('tuple', (1, 2))
- (1, 2)
- >>> vtor.check('tuple', [1, 2])
- (1, 2)
- >>> vtor.check('tuple(3)', (1, 2))
- Traceback (most recent call last):
- VdtValueTooShortError: the value "(1, 2)" is too short.
- >>> vtor.check('tuple(max=5)', (1, 2, 3, 4, 5, 6))
- Traceback (most recent call last):
- VdtValueTooLongError: the value "(1, 2, 3, 4, 5, 6)" is too long.
- >>> vtor.check('tuple(min=3, max=5)', (1, 2, 3, 4))
- (1, 2, 3, 4)
- >>> vtor.check('tuple', 0)
- Traceback (most recent call last):
- VdtTypeError: the value "0" is of the wrong type.
- >>> vtor.check('tuple', '12')
- Traceback (most recent call last):
- VdtTypeError: the value "12" is of the wrong type.
- """
- return tuple(is_list(value, min, max))
- def is_string(value, min=None, max=None):
- """
- Check that the supplied value is a string.
-
- You can optionally specify the minimum and maximum number of members.
-
- >>> vtor.check('string', '0')
- '0'
- >>> vtor.check('string', 0)
- Traceback (most recent call last):
- VdtTypeError: the value "0" is of the wrong type.
- >>> vtor.check('string(2)', '12')
- '12'
- >>> vtor.check('string(2)', '1')
- Traceback (most recent call last):
- VdtValueTooShortError: the value "1" is too short.
- >>> vtor.check('string(min=2, max=3)', '123')
- '123'
- >>> vtor.check('string(min=2, max=3)', '1234')
- Traceback (most recent call last):
- VdtValueTooLongError: the value "1234" is too long.
- """
- if not isinstance(value, basestring):
- raise VdtTypeError(value)
- (min_len, max_len) = _is_num_param(('min', 'max'), (min, max))
- try:
- num_members = len(value)
- except TypeError:
- raise VdtTypeError(value)
- if min_len is not None and num_members < min_len:
- raise VdtValueTooShortError(value)
- if max_len is not None and num_members > max_len:
- raise VdtValueTooLongError(value)
- return value
- def is_int_list(value, min=None, max=None):
- """
- Check that the value is a list of integers.
-
- You can optionally specify the minimum and maximum number of members.
-
- Each list member is checked that it is an integer.
-
- >>> vtor.check('int_list', ())
- []
- >>> vtor.check('int_list', [])
- []
- >>> vtor.check('int_list', (1, 2))
- [1, 2]
- >>> vtor.check('int_list', [1, 2])
- [1, 2]
- >>> vtor.check('int_list', [1, 'a'])
- Traceback (most recent call last):
- VdtTypeError: the value "a" is of the wrong type.
- """
- return [is_integer(mem) for mem in is_list(value, min, max)]
- def is_bool_list(value, min=None, max=None):
- """
- Check that the value is a list of booleans.
-
- You can optionally specify the minimum and maximum number of members.
-
- Each list member is checked that it is a boolean.
-
- >>> vtor.check('bool_list', ())
- []
- >>> vtor.check('bool_list', [])
- []
- >>> check_res = vtor.check('bool_list', (True, False))
- >>> check_res == [True, False]
- 1
- >>> check_res = vtor.check('bool_list', [True, False])
- >>> check_res == [True, False]
- 1
- >>> vtor.check('bool_list', [True, 'a'])
- Traceback (most recent call last):
- VdtTypeError: the value "a" is of the wrong type.
- """
- return [is_boolean(mem) for mem in is_list(value, min, max)]
- def is_float_list(value, min=None, max=None):
- """
- Check that the value is a list of floats.
-
- You can optionally specify the minimum and maximum number of members.
-
- Each list member is checked that it is a float.
-
- >>> vtor.check('float_list', ())
- []
- >>> vtor.check('float_list', [])
- []
- >>> vtor.check('float_list', (1, 2.0))
- [1.0, 2.0]
- >>> vtor.check('float_list', [1, 2.0])
- [1.0, 2.0]
- >>> vtor.check('float_list', [1, 'a'])
- Traceback (most recent call last):
- VdtTypeError: the value "a" is of the wrong type.
- """
- return [is_float(mem) for mem in is_list(value, min, max)]
- def is_string_list(value, min=None, max=None):
- """
- Check that the value is a list of strings.
-
- You can optionally specify the minimum and maximum number of members.
-
- Each list member is checked that it is a string.
-
- >>> vtor.check('string_list', ())
- []
- >>> vtor.check('string_list', [])
- []
- >>> vtor.check('string_list', ('a', 'b'))
- ['a', 'b']
- >>> vtor.check('string_list', ['a', 1])
- Traceback (most recent call last):
- VdtTypeError: the value "1" is of the wrong type.
- >>> vtor.check('string_list', 'hello')
- Traceback (most recent call last):
- VdtTypeError: the value "hello" is of the wrong type.
- """
- if isinstance(value, basestring):
- raise VdtTypeError(value)
- return [is_string(mem) for mem in is_list(value, min, max)]
- def is_ip_addr_list(value, min=None, max=None):
- """
- Check that the value is a list of IP addresses.
-
- You can optionally specify the minimum and maximum number of members.
-
- Each list member is checked that it is an IP address.
-
- >>> vtor.check('ip_addr_list', ())
- []
- >>> vtor.check('ip_addr_list', [])
- []
- >>> vtor.check('ip_addr_list', ('1.2.3.4', '5.6.7.8'))
- ['1.2.3.4', '5.6.7.8']
- >>> vtor.check('ip_addr_list', ['a'])
- Traceback (most recent call last):
- VdtValueError: the value "a" is unacceptable.
- """
- return [is_ip_addr(mem) for mem in is_list(value, min, max)]
- def force_list(value, min=None, max=None):
- """
- Check that a value is a list, coercing strings into
- a list with one member. Useful where users forget the
- trailing comma that turns a single value into a list.
-
- You can optionally specify the minimum and maximum number of members.
- A minumum of greater than one will fail if the user only supplies a
- string.
-
- >>> vtor.check('force_list', ())
- []
- >>> vtor.check('force_list', [])
- []
- >>> vtor.check('force_list', 'hello')
- ['hello']
- """
- if not isinstance(value, (list, tuple)):
- value = [value]
- return is_list(value, min, max)
-
-
- fun_dict = {
- 'integer': is_integer,
- 'float': is_float,
- 'ip_addr': is_ip_addr,
- 'string': is_string,
- 'boolean': is_boolean,
- }
- def is_mixed_list(value, *args):
- """
- Check that the value is a list.
- Allow specifying the type of each member.
- Work on lists of specific lengths.
-
- You specify each member as a positional argument specifying type
-
- Each type should be one of the following strings :
- 'integer', 'float', 'ip_addr', 'string', 'boolean'
-
- So you can specify a list of two strings, followed by
- two integers as :
-
- mixed_list('string', 'string', 'integer', 'integer')
-
- The length of the list must match the number of positional
- arguments you supply.
-
- >>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')"
- >>> check_res = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True))
- >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
- 1
- >>> check_res = vtor.check(mix_str, ('1', '2.0', '1.2.3.4', 'a', 'True'))
- >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
- 1
- >>> vtor.check(mix_str, ('b', 2.0, '1.2.3.4', 'a', True))
- Traceback (most recent call last):
- VdtTypeError: the value "b" is of the wrong type.
- >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a'))
- Traceback (most recent call last):
- VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short.
- >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b'))
- Traceback (most recent call last):
- VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long.
- >>> vtor.check(mix_str, 0)
- Traceback (most recent call last):
- VdtTypeError: the value "0" is of the wrong type.
-
- This test requires an elaborate setup, because of a change in error string
- output from the interpreter between Python 2.2 and 2.3 .
-
- >>> res_seq = (
- ... 'passed an incorrect value "',
- ... 'yoda',
- ... '" for parameter "mixed_list".',
- ... )
- >>> res_str = "'".join(res_seq)
- >>> try:
- ... vtor.check('mixed_list("yoda")', ('a'))
- ... except VdtParamError, err:
- ... str(err) == res_str
- 1
- """
- try:
- length = len(value)
- except TypeError:
- raise VdtTypeError(value)
- if length < len(args):
- raise VdtValueTooShortError(value)
- elif length > len(args):
- raise VdtValueTooLongError(value)
- try:
- return [fun_dict[arg](val) for arg, val in zip(args, value)]
- except KeyError, e:
- raise VdtParamError('mixed_list', e)
- def is_option(value, *options):
- """
- This check matches the value to any of a set of options.
-
- >>> vtor.check('option("yoda", "jedi")', 'yoda')
- 'yoda'
- >>> vtor.check('option("yoda", "jedi")', 'jed')
- Traceback (most recent call last):
- VdtValueError: the value "jed" is unacceptable.
- >>> vtor.check('option("yoda", "jedi")', 0)
- Traceback (most recent call last):
- VdtTypeError: the value "0" is of the wrong type.
- """
- if not isinstance(value, basestring):
- raise VdtTypeError(value)
- if not value in options:
- raise VdtValueError(value)
- return value
- def _test(value, *args, **keywargs):
- """
- A function that exists for test purposes.
-
- >>> checks = [
- ... '3, 6, min=1, max=3, test=list(a, b, c)',
- ... '3',
- ... '3, 6',
- ... '3,',
- ... 'min=1, test="a b c"',
- ... 'min=5, test="a, b, c"',
- ... 'min=1, max=3, test="a, b, c"',
- ... 'min=-100, test=-99',
- ... 'min=1, max=3',
- ... '3, 6, test="36"',
- ... '3, 6, test="a, b, c"',
- ... '3, max=3, test=list("a", "b", "c")',
- ... '''3, max=3, test=list("'a'", 'b', "x=(c)")''',
- ... "test='x=fish(3)'",
- ... ]
- >>> v = Validator({'test': _test})
- >>> for entry in checks:
- ... print v.check(('test(%s)' % entry), 3)
- (3, ('3', '6'), {'test': ['a', 'b', 'c'], 'max': '3', 'min': '1'})
- (3, ('3',), {})
- (3, ('3', '6'), {})
- (3, ('3',), {})
- (3, (), {'test': 'a b c', 'min': '1'})
- (3, (), {'test': 'a, b, c', 'min': '5'})
- (3, (), {'test': 'a, b, c', 'max': '3', 'min': '1'})
- (3, (), {'test': '-99', 'min': '-100'})
- (3, (), {'max': '3', 'min': '1'})
- (3, ('3', '6'), {'test': '36'})
- (3, ('3', '6'), {'test': 'a, b, c'})
- (3, ('3',), {'test': ['a', 'b', 'c'], 'max': '3'})
- (3, ('3',), {'test': ["'a'", 'b', 'x=(c)'], 'max': '3'})
- (3, (), {'test': 'x=fish(3)'})
-
- >>> v = Validator()
- >>> v.check('integer(default=6)', '3')
- 3
- >>> v.check('integer(default=6)', None, True)
- 6
- >>> v.get_default_value('integer(default=6)')
- 6
- >>> v.get_default_value('float(default=6)')
- 6.0
- >>> v.get_default_value('pass(default=None)')
- >>> v.get_default_value("string(default='None')")
- 'None'
- >>> v.get_default_value('pass')
- Traceback (most recent call last):
- KeyError: 'Check "pass" has no default value.'
- >>> v.get_default_value('pass(default=list(1, 2, 3, 4))')
- ['1', '2', '3', '4']
-
- >>> v = Validator()
- >>> v.check("pass(default=None)", None, True)
- >>> v.check("pass(default='None')", None, True)
- 'None'
- >>> v.check('pass(default="None")', None, True)
- 'None'
- >>> v.check('pass(default=list(1, 2, 3, 4))', None, True)
- ['1', '2', '3', '4']
-
- Bug test for unicode arguments
- >>> v = Validator()
- >>> v.check(u'string(min=4)', u'test')
- u'test'
-
- >>> v = Validator()
- >>> v.get_default_value(u'string(min=4, default="1234")')
- u'1234'
- >>> v.check(u'string(min=4, default="1234")', u'test')
- u'test'
-
- >>> v = Validator()
- >>> default = v.get_default_value('string(default=None)')
- >>> default == None
- 1
- """
- return (value, args, keywargs)
- def _test2():
- """
- >>>
- >>> v = Validator()
- >>> v.get_default_value('string(default="#ff00dd")')
- '#ff00dd'
- >>> v.get_default_value('integer(default=3) # comment')
- 3
- """
- def _test3():
- r"""
- >>> vtor.check('string(default="")', '', missing=True)
- ''
- >>> vtor.check('string(default="\n")', '', missing=True)
- '\n'
- >>> print vtor.check('string(default="\n")', '', missing=True),
- <BLANKLINE>
- >>> vtor.check('string()', '\n')
- '\n'
- >>> vtor.check('string(default="\n\n\n")', '', missing=True)
- '\n\n\n'
- >>> vtor.check('string()', 'random \n text goes here\n\n')
- 'random \n text goes here\n\n'
- >>> vtor.check('string(default=" \nrandom text\ngoes \n here\n\n ")',
- ... '', missing=True)
- ' \nrandom text\ngoes \n here\n\n '
- >>> vtor.check("string(default='\n\n\n')", '', missing=True)
- '\n\n\n'
- >>> vtor.check("option('\n','a','b',default='\n')", '', missing=True)
- '\n'
- >>> vtor.check("string_list()", ['foo', '\n', 'bar'])
- ['foo', '\n', 'bar']
- >>> vtor.check("string_list(default=list('\n'))", '', missing=True)
- ['\n']
- """
-
-
- if __name__ == '__main__':
- # run the code tests in doctest format
- import sys
- import doctest
- m = sys.modules.get('__main__')
- globs = m.__dict__.copy()
- globs.update({
- 'vtor': Validator(),
- })
- doctest.testmod(m, globs=globs)
|