Upgrade configobj to 4.7.2
This commit is contained in:
parent
45708d267b
commit
afdbaa3fe6
|
@ -1,6 +1,6 @@
|
||||||
# configobj.py
|
# configobj.py
|
||||||
# A config file reader/writer that supports nested sections in config files.
|
# A config file reader/writer that supports nested sections in config files.
|
||||||
# Copyright (C) 2005-2009 Michael Foord, Nicola Larosa
|
# Copyright (C) 2005-2010 Michael Foord, Nicola Larosa
|
||||||
# E-mail: fuzzyman AT voidspace DOT org DOT uk
|
# E-mail: fuzzyman AT voidspace DOT org DOT uk
|
||||||
# nico AT tekNico DOT net
|
# nico AT tekNico DOT net
|
||||||
|
|
||||||
|
@ -16,37 +16,17 @@
|
||||||
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
|
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
|
||||||
# Comments, suggestions and bug reports welcome.
|
# Comments, suggestions and bug reports welcome.
|
||||||
|
|
||||||
|
|
||||||
from __future__ import generators
|
from __future__ import generators
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
|
||||||
|
|
||||||
|
|
||||||
|
# imported lazily to avoid startup performance hit if it isn't used
|
||||||
compiler = None
|
compiler = None
|
||||||
try:
|
|
||||||
import compiler
|
|
||||||
except ImportError:
|
|
||||||
# for IronPython
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
|
|
||||||
except ImportError:
|
|
||||||
# Python 2.2 does not have these
|
|
||||||
# UTF-8
|
|
||||||
BOM_UTF8 = '\xef\xbb\xbf'
|
|
||||||
# UTF-16, little endian
|
|
||||||
BOM_UTF16_LE = '\xff\xfe'
|
|
||||||
# UTF-16, big endian
|
|
||||||
BOM_UTF16_BE = '\xfe\xff'
|
|
||||||
if sys.byteorder == 'little':
|
|
||||||
# UTF-16, native endianness
|
|
||||||
BOM_UTF16 = BOM_UTF16_LE
|
|
||||||
else:
|
|
||||||
# UTF-16, native endianness
|
|
||||||
BOM_UTF16 = BOM_UTF16_BE
|
|
||||||
|
|
||||||
# A dictionary mapping BOM to
|
# A dictionary mapping BOM to
|
||||||
# the encoding to decode with, and what to set the
|
# the encoding to decode with, and what to set the
|
||||||
|
@ -100,24 +80,20 @@ wspace_plus = ' \r\n\v\t\'"'
|
||||||
tsquot = '"""%s"""'
|
tsquot = '"""%s"""'
|
||||||
tdquot = "'''%s'''"
|
tdquot = "'''%s'''"
|
||||||
|
|
||||||
try:
|
|
||||||
enumerate
|
|
||||||
except NameError:
|
|
||||||
def enumerate(obj):
|
|
||||||
"""enumerate for Python 2.2."""
|
|
||||||
i = -1
|
|
||||||
for item in obj:
|
|
||||||
i += 1
|
|
||||||
yield i, item
|
|
||||||
|
|
||||||
# Sentinel for use in getattr calls to replace hasattr
|
# Sentinel for use in getattr calls to replace hasattr
|
||||||
MISSING = object()
|
MISSING = object()
|
||||||
|
|
||||||
__version__ = '4.6.0'
|
__version__ = '4.7.2'
|
||||||
|
|
||||||
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
|
try:
|
||||||
|
any
|
||||||
|
except NameError:
|
||||||
|
def any(iterable):
|
||||||
|
for entry in iterable:
|
||||||
|
if entry:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
__docformat__ = "restructuredtext en"
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'__version__',
|
'__version__',
|
||||||
|
@ -137,8 +113,8 @@ __all__ = (
|
||||||
'ReloadError',
|
'ReloadError',
|
||||||
'UnreprError',
|
'UnreprError',
|
||||||
'UnknownType',
|
'UnknownType',
|
||||||
'__docformat__',
|
|
||||||
'flatten_errors',
|
'flatten_errors',
|
||||||
|
'get_extra_values'
|
||||||
)
|
)
|
||||||
|
|
||||||
DEFAULT_INTERPOLATION = 'configparser'
|
DEFAULT_INTERPOLATION = 'configparser'
|
||||||
|
@ -164,9 +140,10 @@ OPTION_DEFAULTS = {
|
||||||
|
|
||||||
|
|
||||||
def getObj(s):
|
def getObj(s):
|
||||||
s = "a=" + s
|
global compiler
|
||||||
if compiler is None:
|
if compiler is None:
|
||||||
raise ImportError('compiler module not available')
|
import compiler
|
||||||
|
s = "a=" + 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]
|
||||||
|
|
||||||
|
@ -309,11 +286,9 @@ class RepeatSectionError(ConfigObjError):
|
||||||
|
|
||||||
class MissingInterpolationOption(InterpolationError):
|
class MissingInterpolationOption(InterpolationError):
|
||||||
"""A value specified for interpolation was missing."""
|
"""A value specified for interpolation was missing."""
|
||||||
|
|
||||||
def __init__(self, option):
|
def __init__(self, option):
|
||||||
InterpolationError.__init__(
|
msg = 'missing option "%s" in interpolation.' % option
|
||||||
self,
|
InterpolationError.__init__(self, msg)
|
||||||
'missing option "%s" in interpolation.' % option)
|
|
||||||
|
|
||||||
|
|
||||||
class UnreprError(ConfigObjError):
|
class UnreprError(ConfigObjError):
|
||||||
|
@ -331,6 +306,7 @@ class InterpolationEngine(object):
|
||||||
|
|
||||||
# compiled regexp to use in self.interpolate()
|
# compiled regexp to use in self.interpolate()
|
||||||
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
|
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
|
||||||
|
_cookie = '%'
|
||||||
|
|
||||||
def __init__(self, section):
|
def __init__(self, section):
|
||||||
# the Section instance that "owns" this engine
|
# the Section instance that "owns" this engine
|
||||||
|
@ -338,6 +314,10 @@ class InterpolationEngine(object):
|
||||||
|
|
||||||
|
|
||||||
def interpolate(self, key, value):
|
def interpolate(self, key, value):
|
||||||
|
# short-cut
|
||||||
|
if not self._cookie in value:
|
||||||
|
return value
|
||||||
|
|
||||||
def recursive_interpolate(key, value, section, backtrail):
|
def recursive_interpolate(key, value, section, backtrail):
|
||||||
"""The function that does the actual work.
|
"""The function that does the actual work.
|
||||||
|
|
||||||
|
@ -349,7 +329,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
|
||||||
|
@ -400,11 +380,11 @@ class InterpolationEngine(object):
|
||||||
while True:
|
while True:
|
||||||
# try the current section first
|
# try the current section first
|
||||||
val = current_section.get(key)
|
val = current_section.get(key)
|
||||||
if val is not None:
|
if val is not None and not isinstance(val, Section):
|
||||||
break
|
break
|
||||||
# try "DEFAULT" next
|
# try "DEFAULT" next
|
||||||
val = current_section.get('DEFAULT', {}).get(key)
|
val = current_section.get('DEFAULT', {}).get(key)
|
||||||
if val is not None:
|
if val is not None and not isinstance(val, Section):
|
||||||
break
|
break
|
||||||
# move up to parent and try again
|
# move up to parent and try again
|
||||||
# top-level's parent is itself
|
# top-level's parent is itself
|
||||||
|
@ -442,6 +422,7 @@ class InterpolationEngine(object):
|
||||||
|
|
||||||
class ConfigParserInterpolation(InterpolationEngine):
|
class ConfigParserInterpolation(InterpolationEngine):
|
||||||
"""Behaves like ConfigParser."""
|
"""Behaves like ConfigParser."""
|
||||||
|
_cookie = '%'
|
||||||
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
|
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
|
||||||
|
|
||||||
def _parse_match(self, match):
|
def _parse_match(self, match):
|
||||||
|
@ -453,6 +434,7 @@ class ConfigParserInterpolation(InterpolationEngine):
|
||||||
|
|
||||||
class TemplateInterpolation(InterpolationEngine):
|
class TemplateInterpolation(InterpolationEngine):
|
||||||
"""Behaves like string.Template."""
|
"""Behaves like string.Template."""
|
||||||
|
_cookie = '$'
|
||||||
_delimiter = '$'
|
_delimiter = '$'
|
||||||
_KEYCRE = re.compile(r"""
|
_KEYCRE = re.compile(r"""
|
||||||
\$(?:
|
\$(?:
|
||||||
|
@ -553,6 +535,8 @@ class Section(dict):
|
||||||
# for defaults
|
# for defaults
|
||||||
self.defaults = []
|
self.defaults = []
|
||||||
self.default_values = {}
|
self.default_values = {}
|
||||||
|
self.extra_values = []
|
||||||
|
self._created = False
|
||||||
|
|
||||||
|
|
||||||
def _interpolate(self, key, value):
|
def _interpolate(self, key, value):
|
||||||
|
@ -581,8 +565,17 @@ class Section(dict):
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
"""Fetch the item and do string interpolation."""
|
"""Fetch the item and do string interpolation."""
|
||||||
val = dict.__getitem__(self, key)
|
val = dict.__getitem__(self, key)
|
||||||
if self.main.interpolation and isinstance(val, basestring):
|
if self.main.interpolation:
|
||||||
|
if isinstance(val, basestring):
|
||||||
return self._interpolate(key, val)
|
return self._interpolate(key, val)
|
||||||
|
if isinstance(val, list):
|
||||||
|
def _check(entry):
|
||||||
|
if isinstance(entry, basestring):
|
||||||
|
return self._interpolate(key, entry)
|
||||||
|
return entry
|
||||||
|
new = [_check(entry) for entry in val]
|
||||||
|
if new != val:
|
||||||
|
return new
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
@ -604,7 +597,7 @@ class Section(dict):
|
||||||
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
|
||||||
|
@ -612,13 +605,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__(
|
||||||
|
@ -631,7 +624,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, basestring):
|
if isinstance(value, basestring):
|
||||||
|
@ -672,22 +665,19 @@ class Section(dict):
|
||||||
self[entry] = indict[entry]
|
self[entry] = indict[entry]
|
||||||
|
|
||||||
|
|
||||||
def pop(self, key, *args):
|
def pop(self, key, default=MISSING):
|
||||||
"""
|
"""
|
||||||
'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
|
'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
|
||||||
If key is not found, d is returned if given, otherwise KeyError is raised'
|
If key is not found, d is returned if given, otherwise KeyError is raised'
|
||||||
"""
|
"""
|
||||||
val = dict.pop(self, key, *args)
|
try:
|
||||||
if key in self.scalars:
|
val = self[key]
|
||||||
del self.comments[key]
|
except KeyError:
|
||||||
del self.inline_comments[key]
|
if default is MISSING:
|
||||||
self.scalars.remove(key)
|
raise
|
||||||
elif key in self.sections:
|
val = default
|
||||||
del self.comments[key]
|
else:
|
||||||
del self.inline_comments[key]
|
del self[key]
|
||||||
self.sections.remove(key)
|
|
||||||
if self.main.interpolation and isinstance(val, basestring):
|
|
||||||
return self._interpolate(key, val)
|
|
||||||
return val
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
@ -716,6 +706,8 @@ class Section(dict):
|
||||||
self.comments = {}
|
self.comments = {}
|
||||||
self.inline_comments = {}
|
self.inline_comments = {}
|
||||||
self.configspec = None
|
self.configspec = None
|
||||||
|
self.defaults = []
|
||||||
|
self.extra_values = []
|
||||||
|
|
||||||
|
|
||||||
def setdefault(self, key, default=None):
|
def setdefault(self, key, default=None):
|
||||||
|
@ -761,7 +753,12 @@ class Section(dict):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""x.__repr__() <==> repr(x)"""
|
"""x.__repr__() <==> repr(x)"""
|
||||||
return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
|
def _getval(key):
|
||||||
|
try:
|
||||||
|
return self[key]
|
||||||
|
except MissingInterpolationOption:
|
||||||
|
return dict.__getitem__(self, key)
|
||||||
|
return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(_getval(key))))
|
||||||
for key in (self.scalars + self.sections)])
|
for key in (self.scalars + self.sections)])
|
||||||
|
|
||||||
__str__ = __repr__
|
__str__ = __repr__
|
||||||
|
@ -1148,7 +1145,7 @@ class ConfigObj(Section):
|
||||||
(
|
(
|
||||||
(?:".*?")| # double quotes
|
(?:".*?")| # double quotes
|
||||||
(?:'.*?')| # single quotes
|
(?:'.*?')| # single quotes
|
||||||
(?:[^'",\#].*?) # unquoted
|
(?:[^'",\#]?.*?) # unquoted
|
||||||
)
|
)
|
||||||
\s*,\s* # comma
|
\s*,\s* # comma
|
||||||
''',
|
''',
|
||||||
|
@ -1187,34 +1184,60 @@ class ConfigObj(Section):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, infile=None, options=None, _inspec=False, **kwargs):
|
def __init__(self, infile=None, options=None, configspec=None, encoding=None,
|
||||||
|
interpolation=True, raise_errors=False, list_values=True,
|
||||||
|
create_empty=False, file_error=False, stringify=True,
|
||||||
|
indent_type=None, default_encoding=None, unrepr=False,
|
||||||
|
write_empty_values=False, _inspec=False):
|
||||||
"""
|
"""
|
||||||
Parse a config file or create a config file object.
|
Parse a config file or create a config file object.
|
||||||
|
|
||||||
``ConfigObj(infile=None, options=None, **kwargs)``
|
``ConfigObj(infile=None, configspec=None, encoding=None,
|
||||||
|
interpolation=True, raise_errors=False, list_values=True,
|
||||||
|
create_empty=False, file_error=False, stringify=True,
|
||||||
|
indent_type=None, default_encoding=None, unrepr=False,
|
||||||
|
write_empty_values=False, _inspec=False)``
|
||||||
"""
|
"""
|
||||||
self._inspec = _inspec
|
self._inspec = _inspec
|
||||||
# init the superclass
|
# init the superclass
|
||||||
Section.__init__(self, self, 0, self)
|
Section.__init__(self, self, 0, self)
|
||||||
|
|
||||||
infile = infile or []
|
infile = infile or []
|
||||||
options = dict(options or {})
|
|
||||||
|
|
||||||
# keyword arguments take precedence over an options dictionary
|
_options = {'configspec': configspec,
|
||||||
options.update(kwargs)
|
'encoding': encoding, 'interpolation': interpolation,
|
||||||
|
'raise_errors': raise_errors, 'list_values': list_values,
|
||||||
|
'create_empty': create_empty, 'file_error': file_error,
|
||||||
|
'stringify': stringify, 'indent_type': indent_type,
|
||||||
|
'default_encoding': default_encoding, 'unrepr': unrepr,
|
||||||
|
'write_empty_values': write_empty_values}
|
||||||
|
|
||||||
|
if options is None:
|
||||||
|
options = _options
|
||||||
|
else:
|
||||||
|
import warnings
|
||||||
|
warnings.warn('Passing in an options dictionary to ConfigObj() is '
|
||||||
|
'deprecated. Use **options instead.',
|
||||||
|
DeprecationWarning, stacklevel=2)
|
||||||
|
|
||||||
|
# TODO: check the values too.
|
||||||
|
for entry in options:
|
||||||
|
if entry not in OPTION_DEFAULTS:
|
||||||
|
raise TypeError('Unrecognised option "%s".' % entry)
|
||||||
|
for entry, value in OPTION_DEFAULTS.items():
|
||||||
|
if entry not in options:
|
||||||
|
options[entry] = value
|
||||||
|
keyword_value = _options[entry]
|
||||||
|
if value != keyword_value:
|
||||||
|
options[entry] = keyword_value
|
||||||
|
|
||||||
|
# XXXX this ignores an explicit list_values = True in combination
|
||||||
|
# with _inspec. The user should *never* do that anyway, but still...
|
||||||
if _inspec:
|
if _inspec:
|
||||||
options['list_values'] = False
|
options['list_values'] = False
|
||||||
|
|
||||||
defaults = OPTION_DEFAULTS.copy()
|
self._initialise(options)
|
||||||
# TODO: check the values too.
|
configspec = options['configspec']
|
||||||
for entry in options:
|
|
||||||
if entry not in defaults:
|
|
||||||
raise TypeError('Unrecognised option "%s".' % entry)
|
|
||||||
|
|
||||||
# Add any explicit options to the defaults
|
|
||||||
defaults.update(options)
|
|
||||||
self._initialise(defaults)
|
|
||||||
configspec = defaults['configspec']
|
|
||||||
self._original_configspec = configspec
|
self._original_configspec = configspec
|
||||||
self._load(infile, configspec)
|
self._load(infile, configspec)
|
||||||
|
|
||||||
|
@ -1247,8 +1270,15 @@ class ConfigObj(Section):
|
||||||
# the Section class handles creating subsections
|
# the Section class handles creating subsections
|
||||||
if isinstance(infile, ConfigObj):
|
if isinstance(infile, ConfigObj):
|
||||||
# get a copy of our ConfigObj
|
# get a copy of our ConfigObj
|
||||||
infile = infile.dict()
|
def set_section(in_section, this_section):
|
||||||
|
for entry in in_section.scalars:
|
||||||
|
this_section[entry] = in_section[entry]
|
||||||
|
for section in in_section.sections:
|
||||||
|
this_section[section] = {}
|
||||||
|
set_section(in_section[section], this_section[section])
|
||||||
|
set_section(infile, self)
|
||||||
|
|
||||||
|
else:
|
||||||
for entry in infile:
|
for entry in infile:
|
||||||
self[entry] = infile[entry]
|
self[entry] = infile[entry]
|
||||||
del self._errors
|
del self._errors
|
||||||
|
@ -1342,8 +1372,13 @@ class ConfigObj(Section):
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
def _getval(key):
|
||||||
|
try:
|
||||||
|
return self[key]
|
||||||
|
except MissingInterpolationOption:
|
||||||
|
return dict.__getitem__(self, key)
|
||||||
return ('ConfigObj({%s})' %
|
return ('ConfigObj({%s})' %
|
||||||
', '.join([('%s: %s' % (repr(key), repr(self[key])))
|
', '.join([('%s: %s' % (repr(key), repr(_getval(key))))
|
||||||
for key in (self.scalars + self.sections)]))
|
for key in (self.scalars + self.sections)]))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1560,7 +1595,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('Duplicate section name at line %s.',
|
self._handle_error('Duplicate section name at line %s.',
|
||||||
DuplicateError, infile, cur_index)
|
DuplicateError, infile, cur_index)
|
||||||
continue
|
continue
|
||||||
|
@ -1594,7 +1629,7 @@ class ConfigObj(Section):
|
||||||
# check for a multiline value
|
# check for a multiline value
|
||||||
if value[:3] in ['"""', "'''"]:
|
if value[:3] in ['"""', "'''"]:
|
||||||
try:
|
try:
|
||||||
(value, comment, cur_index) = self._multiline(
|
value, comment, cur_index = self._multiline(
|
||||||
value, infile, cur_index, maxline)
|
value, infile, cur_index, maxline)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
self._handle_error(
|
self._handle_error(
|
||||||
|
@ -1638,7 +1673,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)
|
||||||
|
@ -1703,6 +1738,9 @@ class ConfigObj(Section):
|
||||||
|
|
||||||
def _unquote(self, value):
|
def _unquote(self, value):
|
||||||
"""Return an unquoted version of a value"""
|
"""Return an unquoted version of a value"""
|
||||||
|
if not value:
|
||||||
|
# should only happen during parsing of lists
|
||||||
|
raise SyntaxError
|
||||||
if (value[0] == value[-1]) and (value[0] in ('"', "'")):
|
if (value[0] == value[-1]) and (value[0] in ('"', "'")):
|
||||||
value = value[1:-1]
|
value = value[1:-1]
|
||||||
return value
|
return value
|
||||||
|
@ -1919,6 +1957,7 @@ class ConfigObj(Section):
|
||||||
continue
|
continue
|
||||||
if entry not in section:
|
if entry not in section:
|
||||||
section[entry] = {}
|
section[entry] = {}
|
||||||
|
section[entry]._created = True
|
||||||
if copy:
|
if copy:
|
||||||
# copy comments
|
# copy comments
|
||||||
section.comments[entry] = configspec.comments.get(entry, [])
|
section.comments[entry] = configspec.comments.get(entry, [])
|
||||||
|
@ -1976,6 +2015,8 @@ class ConfigObj(Section):
|
||||||
>>> a.filename = filename
|
>>> a.filename = filename
|
||||||
>>> a == ConfigObj('test.ini', raise_errors=True)
|
>>> a == ConfigObj('test.ini', raise_errors=True)
|
||||||
1
|
1
|
||||||
|
>>> import os
|
||||||
|
>>> os.remove('test.ini')
|
||||||
"""
|
"""
|
||||||
if self.indent_type is None:
|
if self.indent_type is None:
|
||||||
# this can be true if initialised from a dictionary
|
# this can be true if initialised from a dictionary
|
||||||
|
@ -2051,6 +2092,10 @@ class ConfigObj(Section):
|
||||||
|
|
||||||
# Turn the list to a string, joined with correct newlines
|
# Turn the list to a string, joined with correct newlines
|
||||||
newline = self.newlines or os.linesep
|
newline = self.newlines or os.linesep
|
||||||
|
if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w'
|
||||||
|
and sys.platform == 'win32' and newline == '\r\n'):
|
||||||
|
# Windows specific hack to avoid writing '\r\r\n'
|
||||||
|
newline = '\n'
|
||||||
output = self._a_to_u(newline).join(out)
|
output = self._a_to_u(newline).join(out)
|
||||||
if self.encoding:
|
if self.encoding:
|
||||||
output = output.encode(self.encoding)
|
output = output.encode(self.encoding)
|
||||||
|
@ -2124,10 +2169,21 @@ class ConfigObj(Section):
|
||||||
section.indent_type = section.configspec.indent_type
|
section.indent_type = section.configspec.indent_type
|
||||||
|
|
||||||
#
|
#
|
||||||
|
# section.default_values.clear() #??
|
||||||
configspec = section.configspec
|
configspec = section.configspec
|
||||||
self._set_configspec(section, copy)
|
self._set_configspec(section, copy)
|
||||||
|
|
||||||
|
|
||||||
def validate_entry(entry, spec, val, missing, ret_true, ret_false):
|
def validate_entry(entry, spec, val, missing, ret_true, ret_false):
|
||||||
|
section.default_values.pop(entry, None)
|
||||||
|
|
||||||
|
try:
|
||||||
|
section.default_values[entry] = validator.get_default_value(configspec[entry])
|
||||||
|
except (KeyError, AttributeError, validator.baseErrorClass):
|
||||||
|
# No default, bad default or validator has no 'get_default_value'
|
||||||
|
# (e.g. SimpleVal)
|
||||||
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check = validator.check(spec,
|
check = validator.check(spec,
|
||||||
val,
|
val,
|
||||||
|
@ -2142,21 +2198,6 @@ class ConfigObj(Section):
|
||||||
ret_false = False
|
ret_false = False
|
||||||
ret_true = False
|
ret_true = False
|
||||||
else:
|
else:
|
||||||
try:
|
|
||||||
section.default_values.pop(entry, None)
|
|
||||||
except AttributeError:
|
|
||||||
# For Python 2.2 compatibility
|
|
||||||
try:
|
|
||||||
del section.default_values[entry]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
section.default_values[entry] = validator.get_default_value(configspec[entry])
|
|
||||||
except (KeyError, AttributeError):
|
|
||||||
# No default or validator has no 'get_default_value' (e.g. SimpleVal)
|
|
||||||
pass
|
|
||||||
|
|
||||||
ret_false = False
|
ret_false = False
|
||||||
out[entry] = True
|
out[entry] = True
|
||||||
if self.stringify or missing:
|
if self.stringify or missing:
|
||||||
|
@ -2190,13 +2231,12 @@ class ConfigObj(Section):
|
||||||
if entry in ('__many__', '___many___'):
|
if entry in ('__many__', '___many___'):
|
||||||
# reserved names
|
# reserved names
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (not entry in section.scalars) or (entry in section.defaults):
|
if (not entry 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] = (
|
||||||
configspec.comments.get(entry, []))
|
configspec.comments.get(entry, []))
|
||||||
|
@ -2221,6 +2261,7 @@ class ConfigObj(Section):
|
||||||
val = section[entry]
|
val = section[entry]
|
||||||
ret_true, ret_false = validate_entry(entry, many, val, False,
|
ret_true, ret_false = validate_entry(entry, many, val, False,
|
||||||
ret_true, ret_false)
|
ret_true, ret_false)
|
||||||
|
unvalidated = []
|
||||||
|
|
||||||
for entry in incorrect_scalars:
|
for entry in incorrect_scalars:
|
||||||
ret_true = False
|
ret_true = False
|
||||||
|
@ -2246,6 +2287,7 @@ class ConfigObj(Section):
|
||||||
if section is self and entry == 'DEFAULT':
|
if section is self and entry == 'DEFAULT':
|
||||||
continue
|
continue
|
||||||
if section[entry].configspec is None:
|
if section[entry].configspec is None:
|
||||||
|
unvalidated.append(entry)
|
||||||
continue
|
continue
|
||||||
if copy:
|
if copy:
|
||||||
section.comments[entry] = configspec.comments.get(entry, [])
|
section.comments[entry] = configspec.comments.get(entry, [])
|
||||||
|
@ -2258,8 +2300,19 @@ class ConfigObj(Section):
|
||||||
ret_false = False
|
ret_false = False
|
||||||
else:
|
else:
|
||||||
ret_true = False
|
ret_true = False
|
||||||
|
|
||||||
|
section.extra_values = unvalidated
|
||||||
|
if preserve_errors and not section._created:
|
||||||
|
# If the section wasn't created (i.e. it wasn't missing)
|
||||||
|
# then we can't return False, we need to preserve errors
|
||||||
ret_false = False
|
ret_false = False
|
||||||
#
|
#
|
||||||
|
if ret_false and preserve_errors and out:
|
||||||
|
# If we are preserving errors, but all
|
||||||
|
# the failures are from missing sections / values
|
||||||
|
# then we can return False. Otherwise there is a
|
||||||
|
# real failure that we need to preserve.
|
||||||
|
ret_false = not any(out.values())
|
||||||
if ret_true:
|
if ret_true:
|
||||||
return True
|
return True
|
||||||
elif ret_false:
|
elif ret_false:
|
||||||
|
@ -2326,7 +2379,6 @@ class SimpleVal(object):
|
||||||
return member
|
return member
|
||||||
|
|
||||||
|
|
||||||
# Check / processing functions for options
|
|
||||||
def flatten_errors(cfg, res, levels=None, results=None):
|
def flatten_errors(cfg, res, levels=None, results=None):
|
||||||
"""
|
"""
|
||||||
An example function that will turn a nested dictionary of results
|
An example function that will turn a nested dictionary of results
|
||||||
|
@ -2338,9 +2390,7 @@ def flatten_errors(cfg, res, levels=None, results=None):
|
||||||
(This is a recursive function, so you shouldn't use the ``levels`` or
|
(This is a recursive function, so you shouldn't use the ``levels`` or
|
||||||
``results`` arguments - they are used by the function.)
|
``results`` arguments - they are used by the function.)
|
||||||
|
|
||||||
Returns a list of keys that failed. Each member of the list is a tuple :
|
Returns a list of keys that failed. Each member of the list is a tuple::
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
([list of sections...], key, result)
|
([list of sections...], key, result)
|
||||||
|
|
||||||
|
@ -2360,77 +2410,14 @@ def flatten_errors(cfg, res, levels=None, results=None):
|
||||||
object returned. You can use this as a string that describes the failure.
|
object returned. You can use this as a string that describes the failure.
|
||||||
|
|
||||||
For example *The value "3" is of the wrong type*.
|
For example *The value "3" is of the wrong type*.
|
||||||
|
|
||||||
>>> import validate
|
|
||||||
>>> vtor = validate.Validator()
|
|
||||||
>>> my_ini = '''
|
|
||||||
... option1 = True
|
|
||||||
... [section1]
|
|
||||||
... option1 = True
|
|
||||||
... [section2]
|
|
||||||
... another_option = Probably
|
|
||||||
... [section3]
|
|
||||||
... another_option = True
|
|
||||||
... [[section3b]]
|
|
||||||
... value = 3
|
|
||||||
... value2 = a
|
|
||||||
... value3 = 11
|
|
||||||
... '''
|
|
||||||
>>> my_cfg = '''
|
|
||||||
... option1 = boolean()
|
|
||||||
... option2 = boolean()
|
|
||||||
... option3 = boolean(default=Bad_value)
|
|
||||||
... [section1]
|
|
||||||
... option1 = boolean()
|
|
||||||
... option2 = boolean()
|
|
||||||
... option3 = boolean(default=Bad_value)
|
|
||||||
... [section2]
|
|
||||||
... another_option = boolean()
|
|
||||||
... [section3]
|
|
||||||
... another_option = boolean()
|
|
||||||
... [[section3b]]
|
|
||||||
... value = integer
|
|
||||||
... value2 = integer
|
|
||||||
... value3 = integer(0, 10)
|
|
||||||
... [[[section3b-sub]]]
|
|
||||||
... value = string
|
|
||||||
... [section4]
|
|
||||||
... another_option = boolean()
|
|
||||||
... '''
|
|
||||||
>>> cs = my_cfg.split('\\n')
|
|
||||||
>>> ini = my_ini.split('\\n')
|
|
||||||
>>> cfg = ConfigObj(ini, configspec=cs)
|
|
||||||
>>> res = cfg.validate(vtor, preserve_errors=True)
|
|
||||||
>>> errors = []
|
|
||||||
>>> for entry in flatten_errors(cfg, res):
|
|
||||||
... section_list, key, error = entry
|
|
||||||
... section_list.insert(0, '[root]')
|
|
||||||
... if key is not None:
|
|
||||||
... section_list.append(key)
|
|
||||||
... else:
|
|
||||||
... section_list.append('[missing]')
|
|
||||||
... section_string = ', '.join(section_list)
|
|
||||||
... errors.append((section_string, ' = ', error))
|
|
||||||
>>> errors.sort()
|
|
||||||
>>> for entry in errors:
|
|
||||||
... print entry[0], entry[1], (entry[2] or 0)
|
|
||||||
[root], option2 = 0
|
|
||||||
[root], option3 = the value "Bad_value" is of the wrong type.
|
|
||||||
[root], section1, option2 = 0
|
|
||||||
[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], section3, section3b, section3b-sub, [missing] = 0
|
|
||||||
[root], section3, section3b, value2 = the value "a" is of the wrong type.
|
|
||||||
[root], section3, section3b, value3 = the value "11" is too big.
|
|
||||||
[root], section4, [missing] = 0
|
|
||||||
"""
|
"""
|
||||||
if levels is None:
|
if levels is None:
|
||||||
# first time called
|
# first time called
|
||||||
levels = []
|
levels = []
|
||||||
results = []
|
results = []
|
||||||
if res is True:
|
if res == True:
|
||||||
return results
|
return results
|
||||||
if res is False or isinstance(res, Exception):
|
if res == False or isinstance(res, Exception):
|
||||||
results.append((levels[:], None, res))
|
results.append((levels[:], None, res))
|
||||||
if levels:
|
if levels:
|
||||||
levels.pop()
|
levels.pop()
|
||||||
|
@ -2452,4 +2439,30 @@ def flatten_errors(cfg, res, levels=None, results=None):
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def get_extra_values(conf, _prepend=()):
|
||||||
|
"""
|
||||||
|
Find all the values and sections not in the configspec from a validated
|
||||||
|
ConfigObj.
|
||||||
|
|
||||||
|
``get_extra_values`` returns a list of tuples where each tuple represents
|
||||||
|
either an extra section, or an extra value.
|
||||||
|
|
||||||
|
The tuples contain two values, a tuple representing the section the value
|
||||||
|
is in and the name of the extra values. For extra values in the top level
|
||||||
|
section the first member will be an empty tuple. For values in the 'foo'
|
||||||
|
section the first member will be ``('foo',)``. For members in the 'bar'
|
||||||
|
subsection of the 'foo' section the first member will be ``('foo', 'bar')``.
|
||||||
|
|
||||||
|
NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't
|
||||||
|
been validated it will return an empty list.
|
||||||
|
"""
|
||||||
|
out = []
|
||||||
|
|
||||||
|
out.extend([(_prepend, name) for name in conf.extra_values])
|
||||||
|
for name in conf.sections:
|
||||||
|
if name not in conf.extra_values:
|
||||||
|
out.extend(get_extra_values(conf[name], _prepend + (name,)))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
"""*A programming language is a medium of expression.* - Paul Graham"""
|
"""*A programming language is a medium of expression.* - Paul Graham"""
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# validate.py
|
# validate.py
|
||||||
# A Validator object
|
# A Validator object
|
||||||
# Copyright (C) 2005 Michael Foord, Mark Andrews, Nicola Larosa
|
# Copyright (C) 2005-2010 Michael Foord, Mark Andrews, Nicola Larosa
|
||||||
# E-mail: fuzzyman AT voidspace DOT org DOT uk
|
# E-mail: fuzzyman AT voidspace DOT org DOT uk
|
||||||
# mark AT la-la DOT com
|
# mark AT la-la DOT com
|
||||||
# nico AT tekNico DOT net
|
# nico AT tekNico DOT net
|
||||||
|
@ -128,11 +128,8 @@
|
||||||
A badly formatted set of arguments will raise a ``VdtParamError``.
|
A badly formatted set of arguments will raise a ``VdtParamError``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__docformat__ = "restructuredtext en"
|
__version__ = '1.0.1'
|
||||||
|
|
||||||
__version__ = '1.0.0'
|
|
||||||
|
|
||||||
__revision__ = '$Id: validate.py 123 2005-09-08 08:54:28Z fuzzyman $'
|
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'__version__',
|
'__version__',
|
||||||
|
@ -620,7 +617,7 @@ class Validator(object):
|
||||||
fun_kwargs = dict(fun_kwargs)
|
fun_kwargs = dict(fun_kwargs)
|
||||||
else:
|
else:
|
||||||
fun_name, fun_args, fun_kwargs, default = self._parse_check(check)
|
fun_name, fun_args, fun_kwargs, default = self._parse_check(check)
|
||||||
fun_kwargs = dict((str(key), value) for (key, value) in fun_kwargs.items())
|
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
|
self._cache[check] = fun_name, list(fun_args), dict(fun_kwargs), default
|
||||||
return fun_name, fun_args, fun_kwargs, default
|
return fun_name, fun_args, fun_kwargs, default
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue