1
0
Fork 0
mirror of https://github.com/shlomif/PySolFC.git synced 2025-04-15 02:54:09 -04:00
This commit is contained in:
Shlomi Fish 2017-04-17 05:26:40 +03:00
parent ebeec6b751
commit 3be5f2a99a
3 changed files with 163 additions and 103 deletions

View file

@ -19,19 +19,24 @@
from __future__ import generators from __future__ import generators
import sys import sys
import os
import re
from types import StringTypes
from warnings import warn
INTP_VER = sys.version_info[:2] INTP_VER = sys.version_info[:2]
if INTP_VER < (2, 2): if INTP_VER < (2, 2):
raise RuntimeError("Python v.2.2 or later needed") raise RuntimeError("Python v.2.2 or later needed")
import os, re if sys.version_info > (3,):
unicode = str
compiler = None compiler = None
try: try:
import compiler import compiler
except ImportError: except ImportError:
# for IronPython # for IronPython
pass pass
from types import StringTypes
from warnings import warn
try: try:
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
except ImportError: except ImportError:
@ -103,12 +108,6 @@ except NameError:
i += 1 i += 1
yield i, item yield i, item
try:
True, False
except NameError:
True, False = 1, 0
__version__ = '4.4.0' __version__ = '4.4.0'
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $' __revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
@ -164,9 +163,11 @@ def getObj(s):
p = compiler.parse(s) p = compiler.parse(s)
return p.getChildren()[1].getChildren()[0].getChildren()[1] return p.getChildren()[1].getChildren()[0].getChildren()[1]
class UnknownType(Exception): class UnknownType(Exception):
pass pass
class Builder: class Builder:
def build(self, o): def build(self, o):
@ -222,11 +223,13 @@ class Builder:
def build_UnaryAdd(self, o): def build_UnaryAdd(self, o):
return self.build_Const(o.getChildren()[0]) return self.build_Const(o.getChildren()[0])
def unrepr(s): def unrepr(s):
if not s: if not s:
return s return s
return Builder().build(getObj(s)) return Builder().build(getObj(s))
def _splitlines(instring): def _splitlines(instring):
"""Split a string on lines, without losing line endings or truncating.""" """Split a string on lines, without losing line endings or truncating."""
@ -242,11 +245,13 @@ class ConfigObjError(SyntaxError):
self.message = message self.message = message
SyntaxError.__init__(self, message) SyntaxError.__init__(self, message)
class NestingError(ConfigObjError): class NestingError(ConfigObjError):
""" """
This error indicates a level of nesting that doesn't match. This error indicates a level of nesting that doesn't match.
""" """
class ParseError(ConfigObjError): class ParseError(ConfigObjError):
""" """
This error indicates that a line is badly written. This error indicates that a line is badly written.
@ -254,19 +259,23 @@ class ParseError(ConfigObjError):
nor a valid section marker line. nor a valid section marker line.
""" """
class DuplicateError(ConfigObjError): class DuplicateError(ConfigObjError):
""" """
The keyword or section specified already exists. The keyword or section specified already exists.
""" """
class ConfigspecError(ConfigObjError): class ConfigspecError(ConfigObjError):
""" """
An error occurred whilst parsing a configspec. An error occurred whilst parsing a configspec.
""" """
class InterpolationError(ConfigObjError): class InterpolationError(ConfigObjError):
"""Base class for the two interpolation errors.""" """Base class for the two interpolation errors."""
class InterpolationLoopError(InterpolationError): class InterpolationLoopError(InterpolationError):
"""Maximum interpolation depth exceeded in string interpolation.""" """Maximum interpolation depth exceeded in string interpolation."""
@ -275,12 +284,14 @@ class InterpolationLoopError(InterpolationError):
self, self,
'interpolation loop detected in value "%s".' % option) 'interpolation loop detected in value "%s".' % option)
class RepeatSectionError(ConfigObjError): class RepeatSectionError(ConfigObjError):
""" """
This error indicates additional sections in a section with a This error indicates additional sections in a section with a
``__many__`` (repeated) section. ``__many__`` (repeated) section.
""" """
class MissingInterpolationOption(InterpolationError): class MissingInterpolationOption(InterpolationError):
"""A value specified for interpolation was missing.""" """A value specified for interpolation was missing."""
@ -289,6 +300,7 @@ class MissingInterpolationOption(InterpolationError):
self, self,
'missing option "%s" in interpolation.' % option) 'missing option "%s" in interpolation.' % option)
class UnreprError(ConfigObjError): class UnreprError(ConfigObjError):
"""An error parsing in unrepr mode.""" """An error parsing in unrepr mode."""
@ -320,7 +332,7 @@ class InterpolationEngine(object):
This is similar to a depth-first-search algorithm. This is similar to a depth-first-search algorithm.
""" """
# Have we been here already? # Have we been here already?
if backtrail.has_key((key, section.name)): if ((key, section.name) in backtrail):
# Yes - infinite loop detected # Yes - infinite loop detected
raise InterpolationLoopError(key) raise InterpolationLoopError(key)
# Place a marker on our backtrail so we won't come back here again # Place a marker on our backtrail so we won't come back here again
@ -442,11 +454,13 @@ class TemplateInterpolation(InterpolationEngine):
# Anything else: ignore completely, just return it unchanged # Anything else: ignore completely, just return it unchanged
return None, match.group(), None return None, match.group(), None
interpolation_engines = { interpolation_engines = {
'configparser': ConfigParserInterpolation, 'configparser': ConfigParserInterpolation,
'template': TemplateInterpolation, 'template': TemplateInterpolation,
} }
class Section(dict): class Section(dict):
""" """
A dictionary-like object that represents a section in a config file. A dictionary-like object that represents a section in a config file.
@ -512,10 +526,11 @@ class Section(dict):
except AttributeError: except AttributeError:
# not yet: first time running _interpolate(), so pick the engine # not yet: first time running _interpolate(), so pick the engine
name = self.main.interpolation name = self.main.interpolation
if name == True: # note that "if name:" would be incorrect here if name: # note that "if name:" would be incorrect here
# backwards-compatibility: interpolation=True means use default # backwards-compatibility: interpolation=True means use default
name = DEFAULT_INTERPOLATION name = DEFAULT_INTERPOLATION
name = name.lower() # so that "Template", "template", etc. all work # so that "Template", "template", etc. all work
name = name.lower()
class_ = interpolation_engines.get(name, None) class_ = interpolation_engines.get(name, None)
if class_ is None: if class_ is None:
# invalid value for self.main.interpolation # invalid value for self.main.interpolation
@ -549,9 +564,9 @@ class Section(dict):
creating a new sub-section. creating a new sub-section.
""" """
if not isinstance(key, StringTypes): if not isinstance(key, StringTypes):
raise ValueError, 'The key "%s" is not a string.' % key raise ValueError('The key "%s" is not a string.' % key)
# add the comment # add the comment
if not self.comments.has_key(key): if key not in self.comments:
self.comments[key] = [] self.comments[key] = []
self.inline_comments[key] = '' self.inline_comments[key] = ''
# remove the entry from defaults # remove the entry from defaults
@ -559,13 +574,13 @@ class Section(dict):
self.defaults.remove(key) self.defaults.remove(key)
# #
if isinstance(value, Section): if isinstance(value, Section):
if not self.has_key(key): if key not in self:
self.sections.append(key) self.sections.append(key)
dict.__setitem__(self, key, value) dict.__setitem__(self, key, value)
elif isinstance(value, dict) and not unrepr: elif isinstance(value, dict) and not unrepr:
# First create the new depth level, # First create the new depth level,
# then create the section # then create the section
if not self.has_key(key): if key not in self:
self.sections.append(key) self.sections.append(key)
new_depth = self.depth + 1 new_depth = self.depth + 1
dict.__setitem__( dict.__setitem__(
@ -578,7 +593,7 @@ class Section(dict):
indict=value, indict=value,
name=key)) name=key))
else: else:
if not self.has_key(key): if key not in self:
self.scalars.append(key) self.scalars.append(key)
if not self.main.stringify: if not self.main.stringify:
if isinstance(value, StringTypes): if isinstance(value, StringTypes):
@ -586,10 +601,10 @@ class Section(dict):
elif isinstance(value, (list, tuple)): elif isinstance(value, (list, tuple)):
for entry in value: for entry in value:
if not isinstance(entry, StringTypes): if not isinstance(entry, StringTypes):
raise TypeError, ( raise TypeError(
'Value is not a string "%s".' % entry) 'Value is not a string "%s".' % entry)
else: else:
raise TypeError, 'Value is not a string "%s".' % value raise TypeError('Value is not a string "%s".' % value)
dict.__setitem__(self, key, value) dict.__setitem__(self, key, value)
def __delitem__(self, key): def __delitem__(self, key):
@ -635,7 +650,7 @@ class Section(dict):
"""Pops the first (key,val)""" """Pops the first (key,val)"""
sequence = (self.scalars + self.sections) sequence = (self.scalars + self.sections)
if not sequence: if not sequence:
raise KeyError, ": 'popitem(): dictionary is empty'" raise KeyError(": 'popitem(): dictionary is empty'")
key = sequence[0] key = sequence[0]
val = self[key] val = self[key]
del self[key] del self[key]
@ -742,7 +757,8 @@ class Section(dict):
>>> c2 = ConfigObj(a) >>> c2 = ConfigObj(a)
>>> c2.merge(c1) >>> c2.merge(c1)
>>> c2 >>> c2
{'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}} {'section1': {'option1': 'False', 'subsection': \
{'more_options': 'False'}}}
""" """
for key, val in indict.items(): for key, val in indict.items():
if (key in self and isinstance(self[key], dict) and if (key in self and isinstance(self[key], dict) and
@ -765,7 +781,7 @@ class Section(dict):
elif oldkey in self.sections: elif oldkey in self.sections:
the_list = self.sections the_list = self.sections
else: else:
raise KeyError, 'Key "%s" not found.' % oldkey raise KeyError('Key "%s" not found.' % oldkey)
pos = the_list.index(oldkey) pos = the_list.index(oldkey)
# #
val = self[oldkey] val = self[oldkey]
@ -890,6 +906,7 @@ class Section(dict):
1 1
""" """
warn('use of ``decode`` is deprecated.', DeprecationWarning) warn('use of ``decode`` is deprecated.', DeprecationWarning)
def decode(section, key, encoding=encoding, warn=True): def decode(section, key, encoding=encoding, warn=True):
""" """ """ """
val = section[key] val = section[key]
@ -916,6 +933,7 @@ class Section(dict):
Uses the ``walk`` method. Uses the ``walk`` method.
""" """
warn('use of ``encode`` is deprecated.', DeprecationWarning) warn('use of ``encode`` is deprecated.', DeprecationWarning)
def encode(section, key, encoding=encoding): def encode(section, key, encoding=encoding):
""" """ """ """
val = section[key] val = section[key]
@ -947,8 +965,8 @@ class Section(dict):
If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
``True``. ``True``.
If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns If the string is one of ``False``, ``Off``, ``No``, or ``0`` it
``False``. returns ``False``.
``as_bool`` is not case sensitive. ``as_bool`` is not case sensitive.
@ -967,9 +985,9 @@ class Section(dict):
0 0
""" """
val = self[key] val = self[key]
if val == True: if val is True:
return True return True
elif val == False: elif val is False:
return False return False
else: else:
try: try:
@ -1146,7 +1164,7 @@ class ConfigObj(Section):
defaults = OPTION_DEFAULTS.copy() defaults = OPTION_DEFAULTS.copy()
for entry in options.keys(): for entry in options.keys():
if entry not in defaults.keys(): if entry not in defaults.keys():
raise TypeError, 'Unrecognised option "%s".' % entry raise TypeError('Unrecognised option "%s".' % entry)
# TODO: check the values too. # TODO: check the values too.
# #
# Add any explicit options to the defaults # Add any explicit options to the defaults
@ -1180,7 +1198,7 @@ class ConfigObj(Section):
infile = open(infile).read() or [] infile = open(infile).read() or []
elif self.file_error: elif self.file_error:
# raise an error if the file doesn't exist # raise an error if the file doesn't exist
raise IOError, 'Config file not found: "%s".' % self.filename raise IOError('Config file not found: "%s".' % self.filename)
else: else:
# file doesn't already exist # file doesn't already exist
if self.create_empty: if self.create_empty:
@ -1212,7 +1230,7 @@ class ConfigObj(Section):
# needs splitting into lines - but needs doing *after* decoding # needs splitting into lines - but needs doing *after* decoding
# in case it's not an 8 bit encoding # in case it's not an 8 bit encoding
else: else:
raise TypeError, ('infile must be a filename,' raise TypeError('infile must be a filename,'
' file like object, or list of lines.') ' file like object, or list of lines.')
# #
if infile: if infile:
@ -1309,8 +1327,8 @@ class ConfigObj(Section):
# skip UTF8 # skip UTF8
continue continue
if infile.startswith(BOM): if infile.startswith(BOM):
### BOM discovered # BOM discovered
##self.BOM = True # self.BOM = True
# Don't need to remove BOM # Don't need to remove BOM
return self._decode(infile, encoding) return self._decode(infile, encoding)
# #
@ -1383,7 +1401,8 @@ class ConfigObj(Section):
return infile.decode(encoding).splitlines(True) return infile.decode(encoding).splitlines(True)
for i, line in enumerate(infile): for i, line in enumerate(infile):
if not isinstance(line, unicode): if not isinstance(line, unicode):
# NOTE: The isinstance test here handles mixed lists of unicode/string # NOTE: The isinstance test here handles mixed
# lists of unicode/string
# NOTE: But the decode will break on any non-string values # NOTE: But the decode will break on any non-string values
# NOTE: Or could raise a ``UnicodeDecodeError`` # NOTE: Or could raise a ``UnicodeDecodeError``
infile[i] = line.decode(encoding) infile[i] = line.decode(encoding)
@ -1473,7 +1492,7 @@ class ConfigObj(Section):
NestingError, infile, cur_index) NestingError, infile, cur_index)
# #
sect_name = self._unquote(sect_name) sect_name = self._unquote(sect_name)
if parent.has_key(sect_name): if sect_name in parent:
self._handle_error( self._handle_error(
'Duplicate section name at line %s.', 'Duplicate section name at line %s.',
DuplicateError, infile, cur_index) DuplicateError, infile, cur_index)
@ -1519,12 +1538,14 @@ class ConfigObj(Section):
comment = '' comment = ''
try: try:
value = unrepr(value) value = unrepr(value)
except Exception, e: except Exception as e:
if type(e) == UnknownType: if type(e) == UnknownType:
msg = 'Unknown name or type in value at line %s.' msg = 'Unknown name or type ' + \
'in value at line %s.'
else: else:
msg = 'Parse error in value at line %s.' msg = 'Parse error in value at line %s.'
self._handle_error(msg, UnreprError, infile, self._handle_error(
msg, UnreprError, infile,
cur_index) cur_index)
continue continue
else: else:
@ -1532,12 +1553,14 @@ class ConfigObj(Section):
comment = '' comment = ''
try: try:
value = unrepr(value) value = unrepr(value)
except Exception, e: except Exception as e:
if isinstance(e, UnknownType): if isinstance(e, UnknownType):
msg = 'Unknown name or type in value at line %s.' msg = 'Unknown name or type ' + \
'in value at line %s.'
else: else:
msg = 'Parse error in value at line %s.' msg = 'Parse error in value at line %s.'
self._handle_error(msg, UnreprError, infile, self._handle_error(
msg, UnreprError, infile,
cur_index) cur_index)
continue continue
else: else:
@ -1551,7 +1574,7 @@ class ConfigObj(Section):
continue continue
# #
key = self._unquote(key) key = self._unquote(key)
if this_section.has_key(key): if key in this_section:
self._handle_error( self._handle_error(
'Duplicate keyword name at line %s.', 'Duplicate keyword name at line %s.',
DuplicateError, infile, cur_index) DuplicateError, infile, cur_index)
@ -1653,7 +1676,7 @@ class ConfigObj(Section):
if self.stringify: if self.stringify:
value = str(value) value = str(value)
else: else:
raise TypeError, 'Value "%s" is not a string.' % value raise TypeError('Value "%s" is not a string.' % value)
squot = "'%s'" squot = "'%s'"
dquot = '"%s"' dquot = '"%s"'
noquot = "%s" noquot = "%s"
@ -1662,15 +1685,16 @@ class ConfigObj(Section):
tdquot = "'''%s'''" tdquot = "'''%s'''"
if not value: if not value:
return '""' return '""'
if (not self.list_values and '\n' not in value) or not (multiline and if ((not self.list_values and '\n' not in value) or not
((("'" in value) and ('"' in value)) or ('\n' in value))): (multiline and
((("'" in value) and ('"' in value)) or ('\n' in value)))):
if not self.list_values: if not self.list_values:
# we don't quote if ``list_values=False`` # we don't quote if ``list_values=False``
quot = noquot quot = noquot
# for normal values either single or double quotes will do # for normal values either single or double quotes will do
elif '\n' in value: elif '\n' in value:
# will only happen if multiline is off - e.g. '\n' in key # will only happen if multiline is off - e.g. '\n' in key
raise ConfigObjError, ('Value "%s" cannot be safely quoted.' % raise ConfigObjError('Value "%s" cannot be safely quoted.' %
value) value)
elif ((value[0] not in wspace_plus) and elif ((value[0] not in wspace_plus) and
(value[-1] not in wspace_plus) and (value[-1] not in wspace_plus) and
@ -1678,7 +1702,7 @@ class ConfigObj(Section):
quot = noquot quot = noquot
else: else:
if ("'" in value) and ('"' in value): if ("'" in value) and ('"' in value):
raise ConfigObjError, ( raise ConfigObjError(
'Value "%s" cannot be safely quoted.' % value) 'Value "%s" cannot be safely quoted.' % value)
elif '"' in value: elif '"' in value:
quot = squot quot = squot
@ -1689,7 +1713,7 @@ class ConfigObj(Section):
else: else:
# if value has '\n' or "'" *and* '"', it will need triple quotes # if value has '\n' or "'" *and* '"', it will need triple quotes
if (value.find('"""') != -1) and (value.find("'''") != -1): if (value.find('"""') != -1) and (value.find("'''") != -1):
raise ConfigObjError, ( raise ConfigObjError(
'Value "%s" cannot be safely quoted.' % value) 'Value "%s" cannot be safely quoted.' % value)
if value.find('"""') == -1: if value.find('"""') == -1:
quot = tdquot quot = tdquot
@ -1787,11 +1811,11 @@ class ConfigObj(Section):
raise_errors=True, raise_errors=True,
file_error=True, file_error=True,
list_values=False) list_values=False)
except ConfigObjError, e: except ConfigObjError as e:
# FIXME: Should these errors have a reference # FIXME: Should these errors have a reference
# to the already parsed ConfigObj ? # to the already parsed ConfigObj ?
raise ConfigspecError('Parsing configspec failed: %s' % e) raise ConfigspecError('Parsing configspec failed: %s' % e)
except IOError, e: except IOError as e:
raise IOError('Reading configspec failed: %s' % e) raise IOError('Reading configspec failed: %s' % e)
self._set_configspec_value(configspec, self) self._set_configspec_value(configspec, self)
@ -1821,7 +1845,7 @@ class ConfigObj(Section):
section._cs_section_comments[entry] = configspec.comments[entry] section._cs_section_comments[entry] = configspec.comments[entry]
section._cs_section_inline_comments[entry] = ( section._cs_section_inline_comments[entry] = (
configspec.inline_comments[entry]) configspec.inline_comments[entry])
if not section.has_key(entry): if entry not in section:
section[entry] = {} section[entry] = {}
self._set_configspec_value(configspec[entry], section[entry]) self._set_configspec_value(configspec[entry], section[entry])
@ -1852,7 +1876,7 @@ class ConfigObj(Section):
# #
section.configspec = scalars section.configspec = scalars
for entry in sections: for entry in sections:
if not section.has_key(entry): if entry not in section:
section[entry] = {} section[entry] = {}
self._handle_repeat(section[entry], sections[entry]) self._handle_repeat(section[entry], sections[entry])
@ -1967,7 +1991,8 @@ class ConfigObj(Section):
# NOTE: This will *screw* UTF16, each line will start with the BOM # NOTE: This will *screw* UTF16, each line will start with the BOM
if self.encoding: if self.encoding:
out = [l.encode(self.encoding) for l in out] out = [l.encode(self.encoding) for l in out]
if (self.BOM and ((self.encoding is None) or if (self.BOM and
((self.encoding is None) or
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
# Add the UTF8 BOM # Add the UTF8 BOM
if not out: if not out:
@ -1976,11 +2001,11 @@ class ConfigObj(Section):
return out return out
# #
# Turn the list to a string, joined with correct newlines # Turn the list to a string, joined with correct newlines
output = (self._a_to_u(self.newlines or os.linesep) output = (self._a_to_u(self.newlines or os.linesep)).join(out)
).join(out)
if self.encoding: if self.encoding:
output = output.encode(self.encoding) output = output.encode(self.encoding)
if (self.BOM and ((self.encoding is None) or if (self.BOM and
((self.encoding is None) or
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))): (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
# Add the UTF8 BOM # Add the UTF8 BOM
output = BOM_UTF8 + output output = BOM_UTF8 + output
@ -2029,7 +2054,7 @@ class ConfigObj(Section):
""" """
if section is None: if section is None:
if self.configspec is None: if self.configspec is None:
raise ValueError, 'No configspec supplied.' raise ValueError('No configspec supplied.')
if preserve_errors: if preserve_errors:
if VdtMissingValue is None: if VdtMissingValue is None:
raise ImportError('Missing validate module.') raise ImportError('Missing validate module.')
@ -2058,12 +2083,12 @@ class ConfigObj(Section):
for entry in order: for entry in order:
if entry == '__many__': if entry == '__many__':
continue continue
if (not entry in section.scalars) or (entry in section.defaults): if (entry not in section.scalars) or (entry in section.defaults):
# missing entries # missing entries
# or entries from defaults # or entries from defaults
missing = True missing = True
val = None val = None
if copy and not entry in section.scalars: if copy and entry not in section.scalars:
# copy comments # copy comments
section.comments[entry] = ( section.comments[entry] = (
section._configspec_comments.get(entry, [])) section._configspec_comments.get(entry, []))
@ -2078,7 +2103,7 @@ class ConfigObj(Section):
val, val,
missing=missing missing=missing
) )
except validator.baseErrorClass, e: except validator.baseErrorClass as e:
if not preserve_errors or isinstance(e, VdtMissingValue): if not preserve_errors or isinstance(e, VdtMissingValue):
out[entry] = False out[entry] = False
else: else:
@ -2119,9 +2144,9 @@ class ConfigObj(Section):
check = self.validate(validator, preserve_errors=preserve_errors, check = self.validate(validator, preserve_errors=preserve_errors,
copy=copy, section=section[entry]) copy=copy, section=section[entry])
out[entry] = check out[entry] = check
if check == False: if check is False:
ret_true = False ret_true = False
elif check == True: elif check is True:
ret_false = False ret_false = False
else: else:
ret_true = False ret_true = False
@ -2134,6 +2159,7 @@ class ConfigObj(Section):
else: else:
return out return out
class SimpleVal(object): class SimpleVal(object):
""" """
A simple validator. A simple validator.
@ -2155,6 +2181,7 @@ class SimpleVal(object):
raise self.baseErrorClass raise self.baseErrorClass
return member return member
# Check / processing functions for options # Check / processing functions for options
def flatten_errors(cfg, res, levels=None, results=None): def flatten_errors(cfg, res, levels=None, results=None):
""" """
@ -2245,7 +2272,8 @@ def flatten_errors(cfg, res, levels=None, results=None):
[root], option3 = the value "Bad_value" is of the wrong type. [root], option3 = the value "Bad_value" is of the wrong type.
[root], section1, option2 = 0 [root], section1, option2 = 0
[root], section1, option3 = the value "Bad_value" is of the wrong type. [root], section1, option3 = the value "Bad_value" is of the wrong type.
[root], section2, another_option = the value "Probably" is of the wrong type. [root], section2, another_option = the value "Probably" is of
the wrong type.
[root], section3, section3b, section3b-sub, [missing] = 0 [root], section3, section3b, section3b-sub, [missing] = 0
[root], section3, section3b, value2 = the value "a" is of the wrong type. [root], section3, section3b, value2 = the value "a" is of the wrong type.
[root], section3, section3b, value3 = the value "11" is too big. [root], section3, section3b, value3 = the value "11" is too big.
@ -2263,7 +2291,7 @@ def flatten_errors(cfg, res, levels=None, results=None):
levels.pop() levels.pop()
return results return results
for (key, val) in res.items(): for (key, val) in res.items():
if val == True: if val is True:
continue continue
if isinstance(cfg.get(key), dict): if isinstance(cfg.get(key), dict):
# Go down one level # Go down one level
@ -2278,4 +2306,5 @@ def flatten_errors(cfg, res, levels=None, results=None):
# #
return results return results
"""*A programming language is a medium of expression.* - Paul Graham""" """*A programming language is a medium of expression.* - Paul Graham"""

View file

@ -162,11 +162,15 @@ __all__ = (
) )
import sys import sys
import re
INTP_VER = sys.version_info[:2] INTP_VER = sys.version_info[:2]
if INTP_VER < (2, 2): if INTP_VER < (2, 2):
raise RuntimeError("Python v.2.2 or later needed") raise RuntimeError("Python v.2.2 or later needed")
import re if sys.version_info > (3,):
long = int
unicode = str
StringTypes = (str, unicode) StringTypes = (str, unicode)
@ -258,6 +262,7 @@ except NameError:
else: else:
return 0 return 0
def dottedQuadToNum(ip): def dottedQuadToNum(ip):
""" """
Convert decimal dotted quad string to long integer Convert decimal dotted quad string to long integer
@ -281,7 +286,8 @@ def dottedQuadToNum(ip):
""" """
# import here to avoid it when ip_addr values are not used # import here to avoid it when ip_addr values are not used
import socket, struct import socket
import struct
try: try:
return struct.unpack('!L', return struct.unpack('!L',
@ -289,11 +295,12 @@ def dottedQuadToNum(ip):
except socket.error: except socket.error:
# bug in inet_aton, corrected in Python 2.3 # bug in inet_aton, corrected in Python 2.3
if ip.strip() == '255.255.255.255': if ip.strip() == '255.255.255.255':
return 0xFFFFFFFFL return long('0xFFFFFFFF', 0)
else: else:
raise ValueError('Not a good dotted-quad IP: %s' % ip) raise ValueError('Not a good dotted-quad IP: %s' % ip)
return return
def numToDottedQuad(num): def numToDottedQuad(num):
""" """
Convert long int to dotted quad string Convert long int to dotted quad string
@ -317,7 +324,8 @@ def numToDottedQuad(num):
""" """
# import here to avoid it when ip_addr values are not used # import here to avoid it when ip_addr values are not used
import socket, struct import socket
import struct
# no need to intercept here, 4294967295L is fine # no need to intercept here, 4294967295L is fine
try: try:
@ -326,6 +334,7 @@ def numToDottedQuad(num):
except (socket.error, struct.error, OverflowError): except (socket.error, struct.error, OverflowError):
raise ValueError('Not a good numeric IP: %s' % num) raise ValueError('Not a good numeric IP: %s' % num)
class ValidateError(Exception): class ValidateError(Exception):
""" """
This error indicates that the check failed. This error indicates that the check failed.
@ -339,9 +348,11 @@ class ValidateError(Exception):
ValidateError ValidateError
""" """
class VdtMissingValue(ValidateError): class VdtMissingValue(ValidateError):
"""No value was supplied to a check that needed one.""" """No value was supplied to a check that needed one."""
class VdtUnknownCheckError(ValidateError): class VdtUnknownCheckError(ValidateError):
"""An unknown check function was requested""" """An unknown check function was requested"""
@ -355,6 +366,7 @@ class VdtUnknownCheckError(ValidateError):
self, self,
'the check "%s" is unknown.' % value) 'the check "%s" is unknown.' % value)
class VdtParamError(SyntaxError): class VdtParamError(SyntaxError):
"""An incorrect parameter was passed""" """An incorrect parameter was passed"""
@ -369,6 +381,7 @@ class VdtParamError(SyntaxError):
'passed an incorrect value "%s" for parameter "%s".' % ( 'passed an incorrect value "%s" for parameter "%s".' % (
value, name)) value, name))
class VdtTypeError(ValidateError): class VdtTypeError(ValidateError):
"""The value supplied was of the wrong type""" """The value supplied was of the wrong type"""
@ -382,6 +395,7 @@ class VdtTypeError(ValidateError):
self, self,
'the value "%s" is of the wrong type.' % value) 'the value "%s" is of the wrong type.' % value)
class VdtValueError(ValidateError): class VdtValueError(ValidateError):
""" """
The value supplied was of the correct type, but was not an allowed value. The value supplied was of the correct type, but was not an allowed value.
@ -397,6 +411,7 @@ class VdtValueError(ValidateError):
self, self,
'the value "%s" is unacceptable.' % value) 'the value "%s" is unacceptable.' % value)
class VdtValueTooSmallError(VdtValueError): class VdtValueTooSmallError(VdtValueError):
"""The value supplied was of the correct type, but was too small.""" """The value supplied was of the correct type, but was too small."""
@ -410,6 +425,7 @@ class VdtValueTooSmallError(VdtValueError):
self, self,
'the value "%s" is too small.' % value) 'the value "%s" is too small.' % value)
class VdtValueTooBigError(VdtValueError): class VdtValueTooBigError(VdtValueError):
"""The value supplied was of the correct type, but was too big.""" """The value supplied was of the correct type, but was too big."""
@ -423,6 +439,7 @@ class VdtValueTooBigError(VdtValueError):
self, self,
'the value "%s" is too big.' % value) 'the value "%s" is too big.' % value)
class VdtValueTooShortError(VdtValueError): class VdtValueTooShortError(VdtValueError):
"""The value supplied was of the correct type, but was too short.""" """The value supplied was of the correct type, but was too short."""
@ -436,6 +453,7 @@ class VdtValueTooShortError(VdtValueError):
self, self,
'the value "%s" is too short.' % (value,)) 'the value "%s" is too short.' % (value,))
class VdtValueTooLongError(VdtValueError): class VdtValueTooLongError(VdtValueError):
"""The value supplied was of the correct type, but was too long.""" """The value supplied was of the correct type, but was too long."""
@ -449,6 +467,7 @@ class VdtValueTooLongError(VdtValueError):
self, self,
'the value "%s" is too long.' % (value,)) 'the value "%s" is too long.' % (value,))
class Validator(object): class Validator(object):
""" """
Validator is an object that allows you to register a set of 'checks'. Validator is an object that allows you to register a set of 'checks'.
@ -527,7 +546,6 @@ class Validator(object):
# this regex takes apart keyword arguments # this regex takes apart keyword arguments
_key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$') _key_arg = re.compile(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.*)$')
# this regex finds keyword=list(....) type values # this regex finds keyword=list(....) type values
_list_arg = _list_arg _list_arg = _list_arg
@ -539,7 +557,6 @@ class Validator(object):
_paramfinder = re.compile(_paramstring, re.VERBOSE) _paramfinder = re.compile(_paramstring, re.VERBOSE)
_matchfinder = re.compile(_matchstring, re.VERBOSE) _matchfinder = re.compile(_matchstring, re.VERBOSE)
def __init__(self, functions=None): def __init__(self, functions=None):
""" """
>>> vtri = Validator() >>> vtri = Validator()
@ -634,8 +651,6 @@ class Validator(object):
except KeyError: except KeyError:
raise VdtUnknownCheckError(fun_name) raise VdtUnknownCheckError(fun_name)
else: else:
## print fun_args
## print fun_kwargs
return fun(value, *fun_args, **fun_kwargs) return fun(value, *fun_args, **fun_kwargs)
def _unquote(self, val): def _unquote(self, val):
@ -689,7 +704,7 @@ def _is_num_param(names, values, to_float=False):
elif isinstance(val, (int, long, float, StringTypes)): elif isinstance(val, (int, long, float, StringTypes)):
try: try:
out_params.append(fun(val)) out_params.append(fun(val))
except ValueError, e: except ValueError as e:
raise VdtParamError(name, val) raise VdtParamError(name, val)
else: else:
raise VdtParamError(name, val) raise VdtParamError(name, val)
@ -701,6 +716,7 @@ def _is_num_param(names, values, to_float=False):
# note: if the params are specified wrongly in your input string, # note: if the params are specified wrongly in your input string,
# you will also raise errors. # you will also raise errors.
def is_integer(value, min=None, max=None): def is_integer(value, min=None, max=None):
""" """
A check that tests that a given value is an integer (int, or long) A check that tests that a given value is an integer (int, or long)
@ -757,6 +773,7 @@ def is_integer(value, min=None, max=None):
raise VdtValueTooBigError(value) raise VdtValueTooBigError(value)
return value return value
def is_float(value, min=None, max=None): def is_float(value, min=None, max=None):
""" """
A check that tests that a given value is a float A check that tests that a given value is a float
@ -808,11 +825,13 @@ def is_float(value, min=None, max=None):
raise VdtValueTooBigError(value) raise VdtValueTooBigError(value)
return value return value
bool_dict = { bool_dict = {
True: True, 'on': True, '1': True, 'true': True, 'yes': True, True: True, 'on': True, '1': True, 'true': True, 'yes': True,
False: False, 'off': False, '0': False, 'false': False, 'no': False, False: False, 'off': False, '0': False, 'false': False, 'no': False,
} }
def is_boolean(value): def is_boolean(value):
""" """
Check if the value represents a boolean. Check if the value represents a boolean.
@ -865,9 +884,9 @@ def is_boolean(value):
# we do an equality test rather than an identity test # we do an equality test rather than an identity test
# this ensures Python 2.2 compatibility # this ensures Python 2.2 compatibility
# and allows 0 and 1 to represent True and False # and allows 0 and 1 to represent True and False
if value == False: if value is False:
return False return False
elif value == True: elif value is True:
return True return True
else: else:
raise VdtTypeError(value) raise VdtTypeError(value)
@ -912,6 +931,7 @@ def is_ip_addr(value):
raise VdtValueError(value) raise VdtValueError(value)
return value return value
def is_list(value, min=None, max=None): def is_list(value, min=None, max=None):
""" """
Check that the value is a list of values. Check that the value is a list of values.
@ -953,6 +973,7 @@ def is_list(value, min=None, max=None):
raise VdtValueTooLongError(value) raise VdtValueTooLongError(value)
return value return value
def is_string(value, min=None, max=None): def is_string(value, min=None, max=None):
""" """
Check that the supplied value is a string. Check that the supplied value is a string.
@ -988,6 +1009,7 @@ def is_string(value, min=None, max=None):
raise VdtValueTooLongError(value) raise VdtValueTooLongError(value)
return value return value
def is_int_list(value, min=None, max=None): def is_int_list(value, min=None, max=None):
""" """
Check that the value is a list of integers. Check that the value is a list of integers.
@ -1010,6 +1032,7 @@ def is_int_list(value, min=None, max=None):
""" """
return [is_integer(mem) for mem in is_list(value, min, max)] return [is_integer(mem) for mem in is_list(value, min, max)]
def is_bool_list(value, min=None, max=None): def is_bool_list(value, min=None, max=None):
""" """
Check that the value is a list of booleans. Check that the value is a list of booleans.
@ -1034,6 +1057,7 @@ def is_bool_list(value, min=None, max=None):
""" """
return [is_boolean(mem) for mem in is_list(value, min, max)] return [is_boolean(mem) for mem in is_list(value, min, max)]
def is_float_list(value, min=None, max=None): def is_float_list(value, min=None, max=None):
""" """
Check that the value is a list of floats. Check that the value is a list of floats.
@ -1056,6 +1080,7 @@ def is_float_list(value, min=None, max=None):
""" """
return [is_float(mem) for mem in is_list(value, min, max)] return [is_float(mem) for mem in is_list(value, min, max)]
def is_string_list(value, min=None, max=None): def is_string_list(value, min=None, max=None):
""" """
Check that the value is a list of strings. Check that the value is a list of strings.
@ -1081,6 +1106,7 @@ def is_string_list(value, min=None, max=None):
raise VdtTypeError(value) raise VdtTypeError(value)
return [is_string(mem) for mem in is_list(value, min, max)] return [is_string(mem) for mem in is_list(value, min, max)]
def is_ip_addr_list(value, min=None, max=None): def is_ip_addr_list(value, min=None, max=None):
""" """
Check that the value is a list of IP addresses. Check that the value is a list of IP addresses.
@ -1101,6 +1127,7 @@ def is_ip_addr_list(value, min=None, max=None):
""" """
return [is_ip_addr(mem) for mem in is_list(value, min, max)] return [is_ip_addr(mem) for mem in is_list(value, min, max)]
fun_dict = { fun_dict = {
'integer': is_integer, 'integer': is_integer,
'float': is_float, 'float': is_float,
@ -1109,6 +1136,7 @@ fun_dict = {
'boolean': is_boolean, 'boolean': is_boolean,
} }
def is_mixed_list(value, *args): def is_mixed_list(value, *args):
""" """
Check that the value is a list. Check that the value is a list.
@ -1128,7 +1156,8 @@ def is_mixed_list(value, *args):
The length of the list must match the number of positional The length of the list must match the number of positional
arguments you supply. arguments you supply.
>>> mix_str = "mixed_list('integer', 'float', 'ip_addr', 'string', 'boolean')" >>> 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 = vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', True))
>>> check_res == [1, 2.0, '1.2.3.4', 'a', True] >>> check_res == [1, 2.0, '1.2.3.4', 'a', True]
1 1
@ -1143,7 +1172,8 @@ def is_mixed_list(value, *args):
VdtValueTooShortError: the value "(1, 2.0, '1.2.3.4', 'a')" is too short. 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')) >>> vtor.check(mix_str, (1, 2.0, '1.2.3.4', 'a', 1, 'b'))
Traceback (most recent call last): Traceback (most recent call last):
VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')" is too long. VdtValueTooLongError: the value "(1, 2.0, '1.2.3.4', 'a', 1, 'b')"
is too long.
>>> vtor.check(mix_str, 0) >>> vtor.check(mix_str, 0)
Traceback (most recent call last): Traceback (most recent call last):
VdtTypeError: the value "0" is of the wrong type. VdtTypeError: the value "0" is of the wrong type.
@ -1176,9 +1206,10 @@ def is_mixed_list(value, *args):
raise VdtValueTooLongError(value) raise VdtValueTooLongError(value)
try: try:
return [fun_dict[arg](val) for arg, val in zip(args, value)] return [fun_dict[arg](val) for arg, val in zip(args, value)]
except KeyError, e: except KeyError as e:
raise VdtParamError('mixed_list', e) raise VdtParamError('mixed_list', e)
def is_option(value, *options): def is_option(value, *options):
""" """
This check matches the value to any of a set of options. This check matches the value to any of a set of options.
@ -1194,10 +1225,11 @@ def is_option(value, *options):
""" """
if not isinstance(value, StringTypes): if not isinstance(value, StringTypes):
raise VdtTypeError(value) raise VdtTypeError(value)
if not value in options: if value not in options:
raise VdtValueError(value) raise VdtValueError(value)
return value return value
def _test(value, *args, **keywargs): def _test(value, *args, **keywargs):
""" """
A function that exists for test purposes. A function that exists for test purposes.
@ -1470,4 +1502,3 @@ if __name__ == '__main__':
Code cleanup Code cleanup
""" """

View file

@ -10,7 +10,7 @@ use String::ShellQuote qw/ shell_quote /;
# my $cmd = shell_quote( 'flake8', '.' ); # my $cmd = shell_quote( 'flake8', '.' );
my $cmd = shell_quote( 'flake8', my $cmd = shell_quote( 'flake8',
grep { not($_ eq './pysollib/pysoltk.py') } glob('./pysollib/*.py') ); grep { not($_ eq './pysollib/pysoltk.py') } glob('./pysollib/*.py ./pysollib/configobj/*.py') );
# TEST # TEST
eq_or_diff( scalar(`$cmd`), '', "flake8 is happy with the code." ); eq_or_diff( scalar(`$cmd`), '', "flake8 is happy with the code." );