viff

changeset 972:dda8de7d13a2

Imported ConfigObj 4.5.3 into viff.libs.
author Martin Geisler <mg@daimi.au.dk>
date Wed, 08 Oct 2008 21:07:42 +0200
parents 6d658de4e3d9
children 44f0a5053a4e
files viff/config.py viff/libs/configobj.py
diffstat 2 files changed, 2500 insertions(+), 2 deletions(-) [+]
line diff
     1.1 --- a/viff/config.py	Wed Oct 08 21:06:16 2008 +0200
     1.2 +++ b/viff/config.py	Wed Oct 08 21:07:42 2008 +0200
     1.3 @@ -31,8 +31,7 @@
     1.4  
     1.5  __docformat__ = "restructuredtext"
     1.6  
     1.7 -from configobj import ConfigObj
     1.8 -
     1.9 +from viff.libs.configobj import ConfigObj
    1.10  from viff.prss import generate_subsets, PRF
    1.11  from viff.util import rand
    1.12  from viff import paillier
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/viff/libs/configobj.py	Wed Oct 08 21:07:42 2008 +0200
     2.3 @@ -0,0 +1,2499 @@
     2.4 +# configobj.py
     2.5 +# A config file reader/writer that supports nested sections in config files.
     2.6 +# Copyright (C) 2005-2008 Michael Foord, Nicola Larosa
     2.7 +# E-mail: fuzzyman AT voidspace DOT org DOT uk
     2.8 +#         nico AT tekNico DOT net
     2.9 +
    2.10 +# ConfigObj 4
    2.11 +# http://www.voidspace.org.uk/python/configobj.html
    2.12 +
    2.13 +# Released subject to the BSD License
    2.14 +# Please see http://www.voidspace.org.uk/python/license.shtml
    2.15 +
    2.16 +# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
    2.17 +# For information about bugfixes, updates and support, please join the
    2.18 +# ConfigObj mailing list:
    2.19 +# http://lists.sourceforge.net/lists/listinfo/configobj-develop
    2.20 +# Comments, suggestions and bug reports welcome.
    2.21 +
    2.22 +from __future__ import generators
    2.23 +
    2.24 +import sys
    2.25 +INTP_VER = sys.version_info[:2]
    2.26 +if INTP_VER < (2, 2):
    2.27 +    raise RuntimeError("Python v.2.2 or later needed")
    2.28 +
    2.29 +import os, re
    2.30 +compiler = None
    2.31 +try:
    2.32 +    import compiler
    2.33 +except ImportError:
    2.34 +    # for IronPython
    2.35 +    pass
    2.36 +from types import StringTypes
    2.37 +from warnings import warn
    2.38 +try:
    2.39 +    from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
    2.40 +except ImportError:
    2.41 +    # Python 2.2 does not have these
    2.42 +    # UTF-8
    2.43 +    BOM_UTF8 = '\xef\xbb\xbf'
    2.44 +    # UTF-16, little endian
    2.45 +    BOM_UTF16_LE = '\xff\xfe'
    2.46 +    # UTF-16, big endian
    2.47 +    BOM_UTF16_BE = '\xfe\xff'
    2.48 +    if sys.byteorder == 'little':
    2.49 +        # UTF-16, native endianness
    2.50 +        BOM_UTF16 = BOM_UTF16_LE
    2.51 +    else:
    2.52 +        # UTF-16, native endianness
    2.53 +        BOM_UTF16 = BOM_UTF16_BE
    2.54 +
    2.55 +# A dictionary mapping BOM to
    2.56 +# the encoding to decode with, and what to set the
    2.57 +# encoding attribute to.
    2.58 +BOMS = {
    2.59 +    BOM_UTF8: ('utf_8', None),
    2.60 +    BOM_UTF16_BE: ('utf16_be', 'utf_16'),
    2.61 +    BOM_UTF16_LE: ('utf16_le', 'utf_16'),
    2.62 +    BOM_UTF16: ('utf_16', 'utf_16'),
    2.63 +    }
    2.64 +# All legal variants of the BOM codecs.
    2.65 +# TODO: the list of aliases is not meant to be exhaustive, is there a
    2.66 +#   better way ?
    2.67 +BOM_LIST = {
    2.68 +    'utf_16': 'utf_16',
    2.69 +    'u16': 'utf_16',
    2.70 +    'utf16': 'utf_16',
    2.71 +    'utf-16': 'utf_16',
    2.72 +    'utf16_be': 'utf16_be',
    2.73 +    'utf_16_be': 'utf16_be',
    2.74 +    'utf-16be': 'utf16_be',
    2.75 +    'utf16_le': 'utf16_le',
    2.76 +    'utf_16_le': 'utf16_le',
    2.77 +    'utf-16le': 'utf16_le',
    2.78 +    'utf_8': 'utf_8',
    2.79 +    'u8': 'utf_8',
    2.80 +    'utf': 'utf_8',
    2.81 +    'utf8': 'utf_8',
    2.82 +    'utf-8': 'utf_8',
    2.83 +    }
    2.84 +
    2.85 +# Map of encodings to the BOM to write.
    2.86 +BOM_SET = {
    2.87 +    'utf_8': BOM_UTF8,
    2.88 +    'utf_16': BOM_UTF16,
    2.89 +    'utf16_be': BOM_UTF16_BE,
    2.90 +    'utf16_le': BOM_UTF16_LE,
    2.91 +    None: BOM_UTF8
    2.92 +    }
    2.93 +
    2.94 +
    2.95 +def match_utf8(encoding):
    2.96 +    return BOM_LIST.get(encoding.lower()) == 'utf_8'
    2.97 +
    2.98 +
    2.99 +# Quote strings used for writing values
   2.100 +squot = "'%s'"
   2.101 +dquot = '"%s"'
   2.102 +noquot = "%s"
   2.103 +wspace_plus = ' \r\t\n\v\t\'"'
   2.104 +tsquot = '"""%s"""'
   2.105 +tdquot = "'''%s'''"
   2.106 +
   2.107 +try:
   2.108 +    enumerate
   2.109 +except NameError:
   2.110 +    def enumerate(obj):
   2.111 +        """enumerate for Python 2.2."""
   2.112 +        i = -1
   2.113 +        for item in obj:
   2.114 +            i += 1
   2.115 +            yield i, item
   2.116 +
   2.117 +try:
   2.118 +    True, False
   2.119 +except NameError:
   2.120 +    True, False = 1, 0
   2.121 +
   2.122 +
   2.123 +__version__ = '4.5.3'
   2.124 +
   2.125 +__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
   2.126 +
   2.127 +__docformat__ = "restructuredtext en"
   2.128 +
   2.129 +__all__ = (
   2.130 +    '__version__',
   2.131 +    'DEFAULT_INDENT_TYPE',
   2.132 +    'DEFAULT_INTERPOLATION',
   2.133 +    'ConfigObjError',
   2.134 +    'NestingError',
   2.135 +    'ParseError',
   2.136 +    'DuplicateError',
   2.137 +    'ConfigspecError',
   2.138 +    'ConfigObj',
   2.139 +    'SimpleVal',
   2.140 +    'InterpolationError',
   2.141 +    'InterpolationLoopError',
   2.142 +    'MissingInterpolationOption',
   2.143 +    'RepeatSectionError',
   2.144 +    'ReloadError',
   2.145 +    'UnreprError',
   2.146 +    'UnknownType',
   2.147 +    '__docformat__',
   2.148 +    'flatten_errors',
   2.149 +)
   2.150 +
   2.151 +DEFAULT_INTERPOLATION = 'configparser'
   2.152 +DEFAULT_INDENT_TYPE = '    '
   2.153 +MAX_INTERPOL_DEPTH = 10
   2.154 +
   2.155 +OPTION_DEFAULTS = {
   2.156 +    'interpolation': True,
   2.157 +    'raise_errors': False,
   2.158 +    'list_values': True,
   2.159 +    'create_empty': False,
   2.160 +    'file_error': False,
   2.161 +    'configspec': None,
   2.162 +    'stringify': True,
   2.163 +    # option may be set to one of ('', ' ', '\t')
   2.164 +    'indent_type': None,
   2.165 +    'encoding': None,
   2.166 +    'default_encoding': None,
   2.167 +    'unrepr': False,
   2.168 +    'write_empty_values': False,
   2.169 +}
   2.170 +
   2.171 +
   2.172 +
   2.173 +def getObj(s):
   2.174 +    s = "a=" + s
   2.175 +    if compiler is None:
   2.176 +        raise ImportError('compiler module not available')
   2.177 +    p = compiler.parse(s)
   2.178 +    return p.getChildren()[1].getChildren()[0].getChildren()[1]
   2.179 +
   2.180 +
   2.181 +class UnknownType(Exception):
   2.182 +    pass
   2.183 +
   2.184 +
   2.185 +class Builder(object):
   2.186 +    
   2.187 +    def build(self, o):
   2.188 +        m = getattr(self, 'build_' + o.__class__.__name__, None)
   2.189 +        if m is None:
   2.190 +            raise UnknownType(o.__class__.__name__)
   2.191 +        return m(o)
   2.192 +    
   2.193 +    def build_List(self, o):
   2.194 +        return map(self.build, o.getChildren())
   2.195 +    
   2.196 +    def build_Const(self, o):
   2.197 +        return o.value
   2.198 +    
   2.199 +    def build_Dict(self, o):
   2.200 +        d = {}
   2.201 +        i = iter(map(self.build, o.getChildren()))
   2.202 +        for el in i:
   2.203 +            d[el] = i.next()
   2.204 +        return d
   2.205 +    
   2.206 +    def build_Tuple(self, o):
   2.207 +        return tuple(self.build_List(o))
   2.208 +    
   2.209 +    def build_Name(self, o):
   2.210 +        if o.name == 'None':
   2.211 +            return None
   2.212 +        if o.name == 'True':
   2.213 +            return True
   2.214 +        if o.name == 'False':
   2.215 +            return False
   2.216 +        
   2.217 +        # An undefined Name
   2.218 +        raise UnknownType('Undefined Name')
   2.219 +    
   2.220 +    def build_Add(self, o):
   2.221 +        real, imag = map(self.build_Const, o.getChildren())
   2.222 +        try:
   2.223 +            real = float(real)
   2.224 +        except TypeError:
   2.225 +            raise UnknownType('Add')
   2.226 +        if not isinstance(imag, complex) or imag.real != 0.0:
   2.227 +            raise UnknownType('Add')
   2.228 +        return real+imag
   2.229 +    
   2.230 +    def build_Getattr(self, o):
   2.231 +        parent = self.build(o.expr)
   2.232 +        return getattr(parent, o.attrname)
   2.233 +    
   2.234 +    def build_UnarySub(self, o):
   2.235 +        return -self.build_Const(o.getChildren()[0])
   2.236 +    
   2.237 +    def build_UnaryAdd(self, o):
   2.238 +        return self.build_Const(o.getChildren()[0])
   2.239 +
   2.240 +
   2.241 +_builder = Builder()
   2.242 +
   2.243 +
   2.244 +def unrepr(s):
   2.245 +    if not s:
   2.246 +        return s
   2.247 +    return _builder.build(getObj(s))
   2.248 +
   2.249 +
   2.250 +
   2.251 +class ConfigObjError(SyntaxError):
   2.252 +    """
   2.253 +    This is the base class for all errors that ConfigObj raises.
   2.254 +    It is a subclass of SyntaxError.
   2.255 +    """
   2.256 +    def __init__(self, message='', line_number=None, line=''):
   2.257 +        self.line = line
   2.258 +        self.line_number = line_number
   2.259 +        self.message = message
   2.260 +        SyntaxError.__init__(self, message)
   2.261 +
   2.262 +
   2.263 +class NestingError(ConfigObjError):
   2.264 +    """
   2.265 +    This error indicates a level of nesting that doesn't match.
   2.266 +    """
   2.267 +
   2.268 +
   2.269 +class ParseError(ConfigObjError):
   2.270 +    """
   2.271 +    This error indicates that a line is badly written.
   2.272 +    It is neither a valid ``key = value`` line,
   2.273 +    nor a valid section marker line.
   2.274 +    """
   2.275 +
   2.276 +
   2.277 +class ReloadError(IOError):
   2.278 +    """
   2.279 +    A 'reload' operation failed.
   2.280 +    This exception is a subclass of ``IOError``.
   2.281 +    """
   2.282 +    def __init__(self):
   2.283 +        IOError.__init__(self, 'reload failed, filename is not set.')
   2.284 +
   2.285 +
   2.286 +class DuplicateError(ConfigObjError):
   2.287 +    """
   2.288 +    The keyword or section specified already exists.
   2.289 +    """
   2.290 +
   2.291 +
   2.292 +class ConfigspecError(ConfigObjError):
   2.293 +    """
   2.294 +    An error occured whilst parsing a configspec.
   2.295 +    """
   2.296 +
   2.297 +
   2.298 +class InterpolationError(ConfigObjError):
   2.299 +    """Base class for the two interpolation errors."""
   2.300 +
   2.301 +
   2.302 +class InterpolationLoopError(InterpolationError):
   2.303 +    """Maximum interpolation depth exceeded in string interpolation."""
   2.304 +
   2.305 +    def __init__(self, option):
   2.306 +        InterpolationError.__init__(
   2.307 +            self,
   2.308 +            'interpolation loop detected in value "%s".' % option)
   2.309 +
   2.310 +
   2.311 +class RepeatSectionError(ConfigObjError):
   2.312 +    """
   2.313 +    This error indicates additional sections in a section with a
   2.314 +    ``__many__`` (repeated) section.
   2.315 +    """
   2.316 +
   2.317 +
   2.318 +class MissingInterpolationOption(InterpolationError):
   2.319 +    """A value specified for interpolation was missing."""
   2.320 +
   2.321 +    def __init__(self, option):
   2.322 +        InterpolationError.__init__(
   2.323 +            self,
   2.324 +            'missing option "%s" in interpolation.' % option)
   2.325 +
   2.326 +
   2.327 +class UnreprError(ConfigObjError):
   2.328 +    """An error parsing in unrepr mode."""
   2.329 +
   2.330 +
   2.331 +
   2.332 +class InterpolationEngine(object):
   2.333 +    """
   2.334 +    A helper class to help perform string interpolation.
   2.335 +
   2.336 +    This class is an abstract base class; its descendants perform
   2.337 +    the actual work.
   2.338 +    """
   2.339 +
   2.340 +    # compiled regexp to use in self.interpolate()
   2.341 +    _KEYCRE = re.compile(r"%\(([^)]*)\)s")
   2.342 +
   2.343 +    def __init__(self, section):
   2.344 +        # the Section instance that "owns" this engine
   2.345 +        self.section = section
   2.346 +
   2.347 +
   2.348 +    def interpolate(self, key, value):
   2.349 +        def recursive_interpolate(key, value, section, backtrail):
   2.350 +            """The function that does the actual work.
   2.351 +
   2.352 +            ``value``: the string we're trying to interpolate.
   2.353 +            ``section``: the section in which that string was found
   2.354 +            ``backtrail``: a dict to keep track of where we've been,
   2.355 +            to detect and prevent infinite recursion loops
   2.356 +
   2.357 +            This is similar to a depth-first-search algorithm.
   2.358 +            """
   2.359 +            # Have we been here already?
   2.360 +            if backtrail.has_key((key, section.name)):
   2.361 +                # Yes - infinite loop detected
   2.362 +                raise InterpolationLoopError(key)
   2.363 +            # Place a marker on our backtrail so we won't come back here again
   2.364 +            backtrail[(key, section.name)] = 1
   2.365 +
   2.366 +            # Now start the actual work
   2.367 +            match = self._KEYCRE.search(value)
   2.368 +            while match:
   2.369 +                # The actual parsing of the match is implementation-dependent,
   2.370 +                # so delegate to our helper function
   2.371 +                k, v, s = self._parse_match(match)
   2.372 +                if k is None:
   2.373 +                    # That's the signal that no further interpolation is needed
   2.374 +                    replacement = v
   2.375 +                else:
   2.376 +                    # Further interpolation may be needed to obtain final value
   2.377 +                    replacement = recursive_interpolate(k, v, s, backtrail)
   2.378 +                # Replace the matched string with its final value
   2.379 +                start, end = match.span()
   2.380 +                value = ''.join((value[:start], replacement, value[end:]))
   2.381 +                new_search_start = start + len(replacement)
   2.382 +                # Pick up the next interpolation key, if any, for next time
   2.383 +                # through the while loop
   2.384 +                match = self._KEYCRE.search(value, new_search_start)
   2.385 +
   2.386 +            # Now safe to come back here again; remove marker from backtrail
   2.387 +            del backtrail[(key, section.name)]
   2.388 +
   2.389 +            return value
   2.390 +
   2.391 +        # Back in interpolate(), all we have to do is kick off the recursive
   2.392 +        # function with appropriate starting values
   2.393 +        value = recursive_interpolate(key, value, self.section, {})
   2.394 +        return value
   2.395 +
   2.396 +
   2.397 +    def _fetch(self, key):
   2.398 +        """Helper function to fetch values from owning section.
   2.399 +
   2.400 +        Returns a 2-tuple: the value, and the section where it was found.
   2.401 +        """
   2.402 +        # switch off interpolation before we try and fetch anything !
   2.403 +        save_interp = self.section.main.interpolation
   2.404 +        self.section.main.interpolation = False
   2.405 +
   2.406 +        # Start at section that "owns" this InterpolationEngine
   2.407 +        current_section = self.section
   2.408 +        while True:
   2.409 +            # try the current section first
   2.410 +            val = current_section.get(key)
   2.411 +            if val is not None:
   2.412 +                break
   2.413 +            # try "DEFAULT" next
   2.414 +            val = current_section.get('DEFAULT', {}).get(key)
   2.415 +            if val is not None:
   2.416 +                break
   2.417 +            # move up to parent and try again
   2.418 +            # top-level's parent is itself
   2.419 +            if current_section.parent is current_section:
   2.420 +                # reached top level, time to give up
   2.421 +                break
   2.422 +            current_section = current_section.parent
   2.423 +
   2.424 +        # restore interpolation to previous value before returning
   2.425 +        self.section.main.interpolation = save_interp
   2.426 +        if val is None:
   2.427 +            raise MissingInterpolationOption(key)
   2.428 +        return val, current_section
   2.429 +
   2.430 +
   2.431 +    def _parse_match(self, match):
   2.432 +        """Implementation-dependent helper function.
   2.433 +
   2.434 +        Will be passed a match object corresponding to the interpolation
   2.435 +        key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
   2.436 +        key in the appropriate config file section (using the ``_fetch()``
   2.437 +        helper function) and return a 3-tuple: (key, value, section)
   2.438 +
   2.439 +        ``key`` is the name of the key we're looking for
   2.440 +        ``value`` is the value found for that key
   2.441 +        ``section`` is a reference to the section where it was found
   2.442 +
   2.443 +        ``key`` and ``section`` should be None if no further
   2.444 +        interpolation should be performed on the resulting value
   2.445 +        (e.g., if we interpolated "$$" and returned "$").
   2.446 +        """
   2.447 +        raise NotImplementedError()
   2.448 +    
   2.449 +
   2.450 +
   2.451 +class ConfigParserInterpolation(InterpolationEngine):
   2.452 +    """Behaves like ConfigParser."""
   2.453 +    _KEYCRE = re.compile(r"%\(([^)]*)\)s")
   2.454 +
   2.455 +    def _parse_match(self, match):
   2.456 +        key = match.group(1)
   2.457 +        value, section = self._fetch(key)
   2.458 +        return key, value, section
   2.459 +
   2.460 +
   2.461 +
   2.462 +class TemplateInterpolation(InterpolationEngine):
   2.463 +    """Behaves like string.Template."""
   2.464 +    _delimiter = '$'
   2.465 +    _KEYCRE = re.compile(r"""
   2.466 +        \$(?:
   2.467 +          (?P<escaped>\$)              |   # Two $ signs
   2.468 +          (?P<named>[_a-z][_a-z0-9]*)  |   # $name format
   2.469 +          {(?P<braced>[^}]*)}              # ${name} format
   2.470 +        )
   2.471 +        """, re.IGNORECASE | re.VERBOSE)
   2.472 +
   2.473 +    def _parse_match(self, match):
   2.474 +        # Valid name (in or out of braces): fetch value from section
   2.475 +        key = match.group('named') or match.group('braced')
   2.476 +        if key is not None:
   2.477 +            value, section = self._fetch(key)
   2.478 +            return key, value, section
   2.479 +        # Escaped delimiter (e.g., $$): return single delimiter
   2.480 +        if match.group('escaped') is not None:
   2.481 +            # Return None for key and section to indicate it's time to stop
   2.482 +            return None, self._delimiter, None
   2.483 +        # Anything else: ignore completely, just return it unchanged
   2.484 +        return None, match.group(), None
   2.485 +
   2.486 +
   2.487 +interpolation_engines = {
   2.488 +    'configparser': ConfigParserInterpolation,
   2.489 +    'template': TemplateInterpolation,
   2.490 +}
   2.491 +
   2.492 +
   2.493 +
   2.494 +class Section(dict):
   2.495 +    """
   2.496 +    A dictionary-like object that represents a section in a config file.
   2.497 +    
   2.498 +    It does string interpolation if the 'interpolation' attribute
   2.499 +    of the 'main' object is set to True.
   2.500 +    
   2.501 +    Interpolation is tried first from this object, then from the 'DEFAULT'
   2.502 +    section of this object, next from the parent and its 'DEFAULT' section,
   2.503 +    and so on until the main object is reached.
   2.504 +    
   2.505 +    A Section will behave like an ordered dictionary - following the
   2.506 +    order of the ``scalars`` and ``sections`` attributes.
   2.507 +    You can use this to change the order of members.
   2.508 +    
   2.509 +    Iteration follows the order: scalars, then sections.
   2.510 +    """
   2.511 +
   2.512 +    def __init__(self, parent, depth, main, indict=None, name=None):
   2.513 +        """
   2.514 +        * parent is the section above
   2.515 +        * depth is the depth level of this section
   2.516 +        * main is the main ConfigObj
   2.517 +        * indict is a dictionary to initialise the section with
   2.518 +        """
   2.519 +        if indict is None:
   2.520 +            indict = {}
   2.521 +        dict.__init__(self)
   2.522 +        # used for nesting level *and* interpolation
   2.523 +        self.parent = parent
   2.524 +        # used for the interpolation attribute
   2.525 +        self.main = main
   2.526 +        # level of nesting depth of this Section
   2.527 +        self.depth = depth
   2.528 +        # purely for information
   2.529 +        self.name = name
   2.530 +        #
   2.531 +        self._initialise()
   2.532 +        # we do this explicitly so that __setitem__ is used properly
   2.533 +        # (rather than just passing to ``dict.__init__``)
   2.534 +        for entry, value in indict.iteritems():
   2.535 +            self[entry] = value
   2.536 +            
   2.537 +            
   2.538 +    def _initialise(self):
   2.539 +        # the sequence of scalar values in this Section
   2.540 +        self.scalars = []
   2.541 +        # the sequence of sections in this Section
   2.542 +        self.sections = []
   2.543 +        # for comments :-)
   2.544 +        self.comments = {}
   2.545 +        self.inline_comments = {}
   2.546 +        # for the configspec
   2.547 +        self.configspec = {}
   2.548 +        self._order = []
   2.549 +        self._configspec_comments = {}
   2.550 +        self._configspec_inline_comments = {}
   2.551 +        self._cs_section_comments = {}
   2.552 +        self._cs_section_inline_comments = {}
   2.553 +        # for defaults
   2.554 +        self.defaults = []
   2.555 +        self.default_values = {}
   2.556 +
   2.557 +
   2.558 +    def _interpolate(self, key, value):
   2.559 +        try:
   2.560 +            # do we already have an interpolation engine?
   2.561 +            engine = self._interpolation_engine
   2.562 +        except AttributeError:
   2.563 +            # not yet: first time running _interpolate(), so pick the engine
   2.564 +            name = self.main.interpolation
   2.565 +            if name == True:  # note that "if name:" would be incorrect here
   2.566 +                # backwards-compatibility: interpolation=True means use default
   2.567 +                name = DEFAULT_INTERPOLATION
   2.568 +            name = name.lower()  # so that "Template", "template", etc. all work
   2.569 +            class_ = interpolation_engines.get(name, None)
   2.570 +            if class_ is None:
   2.571 +                # invalid value for self.main.interpolation
   2.572 +                self.main.interpolation = False
   2.573 +                return value
   2.574 +            else:
   2.575 +                # save reference to engine so we don't have to do this again
   2.576 +                engine = self._interpolation_engine = class_(self)
   2.577 +        # let the engine do the actual work
   2.578 +        return engine.interpolate(key, value)
   2.579 +
   2.580 +
   2.581 +    def __getitem__(self, key):
   2.582 +        """Fetch the item and do string interpolation."""
   2.583 +        val = dict.__getitem__(self, key)
   2.584 +        if self.main.interpolation and isinstance(val, StringTypes):
   2.585 +            return self._interpolate(key, val)
   2.586 +        return val
   2.587 +
   2.588 +
   2.589 +    def __setitem__(self, key, value, unrepr=False):
   2.590 +        """
   2.591 +        Correctly set a value.
   2.592 +        
   2.593 +        Making dictionary values Section instances.
   2.594 +        (We have to special case 'Section' instances - which are also dicts)
   2.595 +        
   2.596 +        Keys must be strings.
   2.597 +        Values need only be strings (or lists of strings) if
   2.598 +        ``main.stringify`` is set.
   2.599 +        
   2.600 +        `unrepr`` must be set when setting a value to a dictionary, without
   2.601 +        creating a new sub-section.
   2.602 +        """
   2.603 +        if not isinstance(key, StringTypes):
   2.604 +            raise ValueError('The key "%s" is not a string.' % key)
   2.605 +        
   2.606 +        # add the comment
   2.607 +        if not self.comments.has_key(key):
   2.608 +            self.comments[key] = []
   2.609 +            self.inline_comments[key] = ''
   2.610 +        # remove the entry from defaults
   2.611 +        if key in self.defaults:
   2.612 +            self.defaults.remove(key)
   2.613 +        #
   2.614 +        if isinstance(value, Section):
   2.615 +            if not self.has_key(key):
   2.616 +                self.sections.append(key)
   2.617 +            dict.__setitem__(self, key, value)
   2.618 +        elif isinstance(value, dict) and not unrepr:
   2.619 +            # First create the new depth level,
   2.620 +            # then create the section
   2.621 +            if not self.has_key(key):
   2.622 +                self.sections.append(key)
   2.623 +            new_depth = self.depth + 1
   2.624 +            dict.__setitem__(
   2.625 +                self,
   2.626 +                key,
   2.627 +                Section(
   2.628 +                    self,
   2.629 +                    new_depth,
   2.630 +                    self.main,
   2.631 +                    indict=value,
   2.632 +                    name=key))
   2.633 +        else:
   2.634 +            if not self.has_key(key):
   2.635 +                self.scalars.append(key)
   2.636 +            if not self.main.stringify:
   2.637 +                if isinstance(value, StringTypes):
   2.638 +                    pass
   2.639 +                elif isinstance(value, (list, tuple)):
   2.640 +                    for entry in value:
   2.641 +                        if not isinstance(entry, StringTypes):
   2.642 +                            raise TypeError('Value is not a string "%s".' % entry)
   2.643 +                else:
   2.644 +                    raise TypeError('Value is not a string "%s".' % value)
   2.645 +            dict.__setitem__(self, key, value)
   2.646 +
   2.647 +
   2.648 +    def __delitem__(self, key):
   2.649 +        """Remove items from the sequence when deleting."""
   2.650 +        dict. __delitem__(self, key)
   2.651 +        if key in self.scalars:
   2.652 +            self.scalars.remove(key)
   2.653 +        else:
   2.654 +            self.sections.remove(key)
   2.655 +        del self.comments[key]
   2.656 +        del self.inline_comments[key]
   2.657 +
   2.658 +
   2.659 +    def get(self, key, default=None):
   2.660 +        """A version of ``get`` that doesn't bypass string interpolation."""
   2.661 +        try:
   2.662 +            return self[key]
   2.663 +        except KeyError:
   2.664 +            return default
   2.665 +
   2.666 +
   2.667 +    def update(self, indict):
   2.668 +        """
   2.669 +        A version of update that uses our ``__setitem__``.
   2.670 +        """
   2.671 +        for entry in indict:
   2.672 +            self[entry] = indict[entry]
   2.673 +
   2.674 +
   2.675 +    def pop(self, key, *args):
   2.676 +        """
   2.677 +        'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
   2.678 +        If key is not found, d is returned if given, otherwise KeyError is raised'
   2.679 +        """
   2.680 +        val = dict.pop(self, key, *args)
   2.681 +        if key in self.scalars:
   2.682 +            del self.comments[key]
   2.683 +            del self.inline_comments[key]
   2.684 +            self.scalars.remove(key)
   2.685 +        elif key in self.sections:
   2.686 +            del self.comments[key]
   2.687 +            del self.inline_comments[key]
   2.688 +            self.sections.remove(key)
   2.689 +        if self.main.interpolation and isinstance(val, StringTypes):
   2.690 +            return self._interpolate(key, val)
   2.691 +        return val
   2.692 +
   2.693 +
   2.694 +    def popitem(self):
   2.695 +        """Pops the first (key,val)"""
   2.696 +        sequence = (self.scalars + self.sections)
   2.697 +        if not sequence:
   2.698 +            raise KeyError(": 'popitem(): dictionary is empty'")
   2.699 +        key = sequence[0]
   2.700 +        val =  self[key]
   2.701 +        del self[key]
   2.702 +        return key, val
   2.703 +
   2.704 +
   2.705 +    def clear(self):
   2.706 +        """
   2.707 +        A version of clear that also affects scalars/sections
   2.708 +        Also clears comments and configspec.
   2.709 +        
   2.710 +        Leaves other attributes alone :
   2.711 +            depth/main/parent are not affected
   2.712 +        """
   2.713 +        dict.clear(self)
   2.714 +        self.scalars = []
   2.715 +        self.sections = []
   2.716 +        self.comments = {}
   2.717 +        self.inline_comments = {}
   2.718 +        self.configspec = {}
   2.719 +
   2.720 +
   2.721 +    def setdefault(self, key, default=None):
   2.722 +        """A version of setdefault that sets sequence if appropriate."""
   2.723 +        try:
   2.724 +            return self[key]
   2.725 +        except KeyError:
   2.726 +            self[key] = default
   2.727 +            return self[key]
   2.728 +
   2.729 +
   2.730 +    def items(self):
   2.731 +        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
   2.732 +        return zip((self.scalars + self.sections), self.values())
   2.733 +
   2.734 +
   2.735 +    def keys(self):
   2.736 +        """D.keys() -> list of D's keys"""
   2.737 +        return (self.scalars + self.sections)
   2.738 +
   2.739 +
   2.740 +    def values(self):
   2.741 +        """D.values() -> list of D's values"""
   2.742 +        return [self[key] for key in (self.scalars + self.sections)]
   2.743 +
   2.744 +
   2.745 +    def iteritems(self):
   2.746 +        """D.iteritems() -> an iterator over the (key, value) items of D"""
   2.747 +        return iter(self.items())
   2.748 +
   2.749 +
   2.750 +    def iterkeys(self):
   2.751 +        """D.iterkeys() -> an iterator over the keys of D"""
   2.752 +        return iter((self.scalars + self.sections))
   2.753 +
   2.754 +    __iter__ = iterkeys
   2.755 +
   2.756 +
   2.757 +    def itervalues(self):
   2.758 +        """D.itervalues() -> an iterator over the values of D"""
   2.759 +        return iter(self.values())
   2.760 +
   2.761 +
   2.762 +    def __repr__(self):
   2.763 +        """x.__repr__() <==> repr(x)"""
   2.764 +        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
   2.765 +            for key in (self.scalars + self.sections)])
   2.766 +
   2.767 +    __str__ = __repr__
   2.768 +    __str__.__doc__ = "x.__str__() <==> str(x)"
   2.769 +
   2.770 +
   2.771 +    # Extra methods - not in a normal dictionary
   2.772 +
   2.773 +    def dict(self):
   2.774 +        """
   2.775 +        Return a deepcopy of self as a dictionary.
   2.776 +        
   2.777 +        All members that are ``Section`` instances are recursively turned to
   2.778 +        ordinary dictionaries - by calling their ``dict`` method.
   2.779 +        
   2.780 +        >>> n = a.dict()
   2.781 +        >>> n == a
   2.782 +        1
   2.783 +        >>> n is a
   2.784 +        0
   2.785 +        """
   2.786 +        newdict = {}
   2.787 +        for entry in self:
   2.788 +            this_entry = self[entry]
   2.789 +            if isinstance(this_entry, Section):
   2.790 +                this_entry = this_entry.dict()
   2.791 +            elif isinstance(this_entry, list):
   2.792 +                # create a copy rather than a reference
   2.793 +                this_entry = list(this_entry)
   2.794 +            elif isinstance(this_entry, tuple):
   2.795 +                # create a copy rather than a reference
   2.796 +                this_entry = tuple(this_entry)
   2.797 +            newdict[entry] = this_entry
   2.798 +        return newdict
   2.799 +
   2.800 +
   2.801 +    def merge(self, indict):
   2.802 +        """
   2.803 +        A recursive update - useful for merging config files.
   2.804 +        
   2.805 +        >>> a = '''[section1]
   2.806 +        ...     option1 = True
   2.807 +        ...     [[subsection]]
   2.808 +        ...     more_options = False
   2.809 +        ...     # end of file'''.splitlines()
   2.810 +        >>> b = '''# File is user.ini
   2.811 +        ...     [section1]
   2.812 +        ...     option1 = False
   2.813 +        ...     # end of file'''.splitlines()
   2.814 +        >>> c1 = ConfigObj(b)
   2.815 +        >>> c2 = ConfigObj(a)
   2.816 +        >>> c2.merge(c1)
   2.817 +        >>> c2
   2.818 +        {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
   2.819 +        """
   2.820 +        for key, val in indict.items():
   2.821 +            if (key in self and isinstance(self[key], dict) and
   2.822 +                                isinstance(val, dict)):
   2.823 +                self[key].merge(val)
   2.824 +            else:   
   2.825 +                self[key] = val
   2.826 +
   2.827 +
   2.828 +    def rename(self, oldkey, newkey):
   2.829 +        """
   2.830 +        Change a keyname to another, without changing position in sequence.
   2.831 +        
   2.832 +        Implemented so that transformations can be made on keys,
   2.833 +        as well as on values. (used by encode and decode)
   2.834 +        
   2.835 +        Also renames comments.
   2.836 +        """
   2.837 +        if oldkey in self.scalars:
   2.838 +            the_list = self.scalars
   2.839 +        elif oldkey in self.sections:
   2.840 +            the_list = self.sections
   2.841 +        else:
   2.842 +            raise KeyError('Key "%s" not found.' % oldkey)
   2.843 +        pos = the_list.index(oldkey)
   2.844 +        #
   2.845 +        val = self[oldkey]
   2.846 +        dict.__delitem__(self, oldkey)
   2.847 +        dict.__setitem__(self, newkey, val)
   2.848 +        the_list.remove(oldkey)
   2.849 +        the_list.insert(pos, newkey)
   2.850 +        comm = self.comments[oldkey]
   2.851 +        inline_comment = self.inline_comments[oldkey]
   2.852 +        del self.comments[oldkey]
   2.853 +        del self.inline_comments[oldkey]
   2.854 +        self.comments[newkey] = comm
   2.855 +        self.inline_comments[newkey] = inline_comment
   2.856 +
   2.857 +
   2.858 +    def walk(self, function, raise_errors=True,
   2.859 +            call_on_sections=False, **keywargs):
   2.860 +        """
   2.861 +        Walk every member and call a function on the keyword and value.
   2.862 +        
   2.863 +        Return a dictionary of the return values
   2.864 +        
   2.865 +        If the function raises an exception, raise the errror
   2.866 +        unless ``raise_errors=False``, in which case set the return value to
   2.867 +        ``False``.
   2.868 +        
   2.869 +        Any unrecognised keyword arguments you pass to walk, will be pased on
   2.870 +        to the function you pass in.
   2.871 +        
   2.872 +        Note: if ``call_on_sections`` is ``True`` then - on encountering a
   2.873 +        subsection, *first* the function is called for the *whole* subsection,
   2.874 +        and then recurses into it's members. This means your function must be
   2.875 +        able to handle strings, dictionaries and lists. This allows you
   2.876 +        to change the key of subsections as well as for ordinary members. The
   2.877 +        return value when called on the whole subsection has to be discarded.
   2.878 +        
   2.879 +        See  the encode and decode methods for examples, including functions.
   2.880 +        
   2.881 +        .. caution::
   2.882 +        
   2.883 +            You can use ``walk`` to transform the names of members of a section
   2.884 +            but you mustn't add or delete members.
   2.885 +        
   2.886 +        >>> config = '''[XXXXsection]
   2.887 +        ... XXXXkey = XXXXvalue'''.splitlines()
   2.888 +        >>> cfg = ConfigObj(config)
   2.889 +        >>> cfg
   2.890 +        {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
   2.891 +        >>> def transform(section, key):
   2.892 +        ...     val = section[key]
   2.893 +        ...     newkey = key.replace('XXXX', 'CLIENT1')
   2.894 +        ...     section.rename(key, newkey)
   2.895 +        ...     if isinstance(val, (tuple, list, dict)):
   2.896 +        ...         pass
   2.897 +        ...     else:
   2.898 +        ...         val = val.replace('XXXX', 'CLIENT1')
   2.899 +        ...         section[newkey] = val
   2.900 +        >>> cfg.walk(transform, call_on_sections=True)
   2.901 +        {'CLIENT1section': {'CLIENT1key': None}}
   2.902 +        >>> cfg
   2.903 +        {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
   2.904 +        """
   2.905 +        out = {}
   2.906 +        # scalars first
   2.907 +        for i in range(len(self.scalars)):
   2.908 +            entry = self.scalars[i]
   2.909 +            try:
   2.910 +                val = function(self, entry, **keywargs)
   2.911 +                # bound again in case name has changed
   2.912 +                entry = self.scalars[i]
   2.913 +                out[entry] = val
   2.914 +            except Exception:
   2.915 +                if raise_errors:
   2.916 +                    raise
   2.917 +                else:
   2.918 +                    entry = self.scalars[i]
   2.919 +                    out[entry] = False
   2.920 +        # then sections
   2.921 +        for i in range(len(self.sections)):
   2.922 +            entry = self.sections[i]
   2.923 +            if call_on_sections:
   2.924 +                try:
   2.925 +                    function(self, entry, **keywargs)
   2.926 +                except Exception:
   2.927 +                    if raise_errors:
   2.928 +                        raise
   2.929 +                    else:
   2.930 +                        entry = self.sections[i]
   2.931 +                        out[entry] = False
   2.932 +                # bound again in case name has changed
   2.933 +                entry = self.sections[i]
   2.934 +            # previous result is discarded
   2.935 +            out[entry] = self[entry].walk(
   2.936 +                function,
   2.937 +                raise_errors=raise_errors,
   2.938 +                call_on_sections=call_on_sections,
   2.939 +                **keywargs)
   2.940 +        return out
   2.941 +
   2.942 +
   2.943 +    def decode(self, encoding):
   2.944 +        """
   2.945 +        Decode all strings and values to unicode, using the specified encoding.
   2.946 +        
   2.947 +        Works with subsections and list values.
   2.948 +        
   2.949 +        Uses the ``walk`` method.
   2.950 +        
   2.951 +        Testing ``encode`` and ``decode``.
   2.952 +        >>> m = ConfigObj(a)
   2.953 +        >>> m.decode('ascii')
   2.954 +        >>> def testuni(val):
   2.955 +        ...     for entry in val:
   2.956 +        ...         if not isinstance(entry, unicode):
   2.957 +        ...             print >> sys.stderr, type(entry)
   2.958 +        ...             raise AssertionError, 'decode failed.'
   2.959 +        ...         if isinstance(val[entry], dict):
   2.960 +        ...             testuni(val[entry])
   2.961 +        ...         elif not isinstance(val[entry], unicode):
   2.962 +        ...             raise AssertionError, 'decode failed.'
   2.963 +        >>> testuni(m)
   2.964 +        >>> m.encode('ascii')
   2.965 +        >>> a == m
   2.966 +        1
   2.967 +        """
   2.968 +        warn('use of ``decode`` is deprecated.', DeprecationWarning)
   2.969 +        def decode(section, key, encoding=encoding, warn=True):
   2.970 +            """ """
   2.971 +            val = section[key]
   2.972 +            if isinstance(val, (list, tuple)):
   2.973 +                newval = []
   2.974 +                for entry in val:
   2.975 +                    newval.append(entry.decode(encoding))
   2.976 +            elif isinstance(val, dict):
   2.977 +                newval = val
   2.978 +            else:
   2.979 +                newval = val.decode(encoding)
   2.980 +            newkey = key.decode(encoding)
   2.981 +            section.rename(key, newkey)
   2.982 +            section[newkey] = newval
   2.983 +        # using ``call_on_sections`` allows us to modify section names
   2.984 +        self.walk(decode, call_on_sections=True)
   2.985 +
   2.986 +
   2.987 +    def encode(self, encoding):
   2.988 +        """
   2.989 +        Encode all strings and values from unicode,
   2.990 +        using the specified encoding.
   2.991 +        
   2.992 +        Works with subsections and list values.
   2.993 +        Uses the ``walk`` method.
   2.994 +        """
   2.995 +        warn('use of ``encode`` is deprecated.', DeprecationWarning)
   2.996 +        def encode(section, key, encoding=encoding):
   2.997 +            """ """
   2.998 +            val = section[key]
   2.999 +            if isinstance(val, (list, tuple)):
  2.1000 +                newval = []
  2.1001 +                for entry in val:
  2.1002 +                    newval.append(entry.encode(encoding))
  2.1003 +            elif isinstance(val, dict):
  2.1004 +                newval = val
  2.1005 +            else:
  2.1006 +                newval = val.encode(encoding)
  2.1007 +            newkey = key.encode(encoding)
  2.1008 +            section.rename(key, newkey)
  2.1009 +            section[newkey] = newval
  2.1010 +        self.walk(encode, call_on_sections=True)
  2.1011 +
  2.1012 +
  2.1013 +    def istrue(self, key):
  2.1014 +        """A deprecated version of ``as_bool``."""
  2.1015 +        warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
  2.1016 +                'instead.', DeprecationWarning)
  2.1017 +        return self.as_bool(key)
  2.1018 +
  2.1019 +
  2.1020 +    def as_bool(self, key):
  2.1021 +        """
  2.1022 +        Accepts a key as input. The corresponding value must be a string or
  2.1023 +        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
  2.1024 +        retain compatibility with Python 2.2.
  2.1025 +        
  2.1026 +        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
  2.1027 +        ``True``.
  2.1028 +        
  2.1029 +        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
  2.1030 +        ``False``.
  2.1031 +        
  2.1032 +        ``as_bool`` is not case sensitive.
  2.1033 +        
  2.1034 +        Any other input will raise a ``ValueError``.
  2.1035 +        
  2.1036 +        >>> a = ConfigObj()
  2.1037 +        >>> a['a'] = 'fish'
  2.1038 +        >>> a.as_bool('a')
  2.1039 +        Traceback (most recent call last):
  2.1040 +        ValueError: Value "fish" is neither True nor False
  2.1041 +        >>> a['b'] = 'True'
  2.1042 +        >>> a.as_bool('b')
  2.1043 +        1
  2.1044 +        >>> a['b'] = 'off'
  2.1045 +        >>> a.as_bool('b')
  2.1046 +        0
  2.1047 +        """
  2.1048 +        val = self[key]
  2.1049 +        if val == True:
  2.1050 +            return True
  2.1051 +        elif val == False:
  2.1052 +            return False
  2.1053 +        else:
  2.1054 +            try:
  2.1055 +                if not isinstance(val, StringTypes):
  2.1056 +                    # TODO: Why do we raise a KeyError here?
  2.1057 +                    raise KeyError()
  2.1058 +                else:
  2.1059 +                    return self.main._bools[val.lower()]
  2.1060 +            except KeyError:
  2.1061 +                raise ValueError('Value "%s" is neither True nor False' % val)
  2.1062 +
  2.1063 +
  2.1064 +    def as_int(self, key):
  2.1065 +        """
  2.1066 +        A convenience method which coerces the specified value to an integer.
  2.1067 +        
  2.1068 +        If the value is an invalid literal for ``int``, a ``ValueError`` will
  2.1069 +        be raised.
  2.1070 +        
  2.1071 +        >>> a = ConfigObj()
  2.1072 +        >>> a['a'] = 'fish'
  2.1073 +        >>> a.as_int('a')
  2.1074 +        Traceback (most recent call last):
  2.1075 +        ValueError: invalid literal for int(): fish
  2.1076 +        >>> a['b'] = '1'
  2.1077 +        >>> a.as_int('b')
  2.1078 +        1
  2.1079 +        >>> a['b'] = '3.2'
  2.1080 +        >>> a.as_int('b')
  2.1081 +        Traceback (most recent call last):
  2.1082 +        ValueError: invalid literal for int(): 3.2
  2.1083 +        """
  2.1084 +        return int(self[key])
  2.1085 +
  2.1086 +
  2.1087 +    def as_float(self, key):
  2.1088 +        """
  2.1089 +        A convenience method which coerces the specified value to a float.
  2.1090 +        
  2.1091 +        If the value is an invalid literal for ``float``, a ``ValueError`` will
  2.1092 +        be raised.
  2.1093 +        
  2.1094 +        >>> a = ConfigObj()
  2.1095 +        >>> a['a'] = 'fish'
  2.1096 +        >>> a.as_float('a')
  2.1097 +        Traceback (most recent call last):
  2.1098 +        ValueError: invalid literal for float(): fish
  2.1099 +        >>> a['b'] = '1'
  2.1100 +        >>> a.as_float('b')
  2.1101 +        1.0
  2.1102 +        >>> a['b'] = '3.2'
  2.1103 +        >>> a.as_float('b')
  2.1104 +        3.2000000000000002
  2.1105 +        """
  2.1106 +        return float(self[key])
  2.1107 +
  2.1108 +
  2.1109 +    def restore_default(self, key):
  2.1110 +        """
  2.1111 +        Restore (and return) default value for the specified key.
  2.1112 +        
  2.1113 +        This method will only work for a ConfigObj that was created
  2.1114 +        with a configspec and has been validated.
  2.1115 +        
  2.1116 +        If there is no default value for this key, ``KeyError`` is raised.
  2.1117 +        """
  2.1118 +        default = self.default_values[key]
  2.1119 +        dict.__setitem__(self, key, default)
  2.1120 +        if key not in self.defaults:
  2.1121 +            self.defaults.append(key)
  2.1122 +        return default
  2.1123 +
  2.1124 +    
  2.1125 +    def restore_defaults(self):
  2.1126 +        """
  2.1127 +        Recursively restore default values to all members
  2.1128 +        that have them.
  2.1129 +        
  2.1130 +        This method will only work for a ConfigObj that was created
  2.1131 +        with a configspec and has been validated.
  2.1132 +        
  2.1133 +        It doesn't delete or modify entries without default values.
  2.1134 +        """
  2.1135 +        for key in self.default_values:
  2.1136 +            self.restore_default(key)
  2.1137 +            
  2.1138 +        for section in self.sections:
  2.1139 +            self[section].restore_defaults()
  2.1140 +
  2.1141 +
  2.1142 +class ConfigObj(Section):
  2.1143 +    """An object to read, create, and write config files."""
  2.1144 +
  2.1145 +    _keyword = re.compile(r'''^ # line start
  2.1146 +        (\s*)                   # indentation
  2.1147 +        (                       # keyword
  2.1148 +            (?:".*?")|          # double quotes
  2.1149 +            (?:'.*?')|          # single quotes
  2.1150 +            (?:[^'"=].*?)       # no quotes
  2.1151 +        )
  2.1152 +        \s*=\s*                 # divider
  2.1153 +        (.*)                    # value (including list values and comments)
  2.1154 +        $   # line end
  2.1155 +        ''',
  2.1156 +        re.VERBOSE)
  2.1157 +
  2.1158 +    _sectionmarker = re.compile(r'''^
  2.1159 +        (\s*)                     # 1: indentation
  2.1160 +        ((?:\[\s*)+)              # 2: section marker open
  2.1161 +        (                         # 3: section name open
  2.1162 +            (?:"\s*\S.*?\s*")|    # at least one non-space with double quotes
  2.1163 +            (?:'\s*\S.*?\s*')|    # at least one non-space with single quotes
  2.1164 +            (?:[^'"\s].*?)        # at least one non-space unquoted
  2.1165 +        )                         # section name close
  2.1166 +        ((?:\s*\])+)              # 4: section marker close
  2.1167 +        \s*(\#.*)?                # 5: optional comment
  2.1168 +        $''',
  2.1169 +        re.VERBOSE)
  2.1170 +
  2.1171 +    # this regexp pulls list values out as a single string
  2.1172 +    # or single values and comments
  2.1173 +    # FIXME: this regex adds a '' to the end of comma terminated lists
  2.1174 +    #   workaround in ``_handle_value``
  2.1175 +    _valueexp = re.compile(r'''^
  2.1176 +        (?:
  2.1177 +            (?:
  2.1178 +                (
  2.1179 +                    (?:
  2.1180 +                        (?:
  2.1181 +                            (?:".*?")|              # double quotes
  2.1182 +                            (?:'.*?')|              # single quotes
  2.1183 +                            (?:[^'",\#][^,\#]*?)    # unquoted
  2.1184 +                        )
  2.1185 +                        \s*,\s*                     # comma
  2.1186 +                    )*      # match all list items ending in a comma (if any)
  2.1187 +                )
  2.1188 +                (
  2.1189 +                    (?:".*?")|                      # double quotes
  2.1190 +                    (?:'.*?')|                      # single quotes
  2.1191 +                    (?:[^'",\#\s][^,]*?)|           # unquoted
  2.1192 +                    (?:(?<!,))                      # Empty value
  2.1193 +                )?          # last item in a list - or string value
  2.1194 +            )|
  2.1195 +            (,)             # alternatively a single comma - empty list
  2.1196 +        )
  2.1197 +        \s*(\#.*)?          # optional comment
  2.1198 +        $''',
  2.1199 +        re.VERBOSE)
  2.1200 +
  2.1201 +    # use findall to get the members of a list value
  2.1202 +    _listvalueexp = re.compile(r'''
  2.1203 +        (
  2.1204 +            (?:".*?")|          # double quotes
  2.1205 +            (?:'.*?')|          # single quotes
  2.1206 +            (?:[^'",\#].*?)       # unquoted
  2.1207 +        )
  2.1208 +        \s*,\s*                 # comma
  2.1209 +        ''',
  2.1210 +        re.VERBOSE)
  2.1211 +
  2.1212 +    # this regexp is used for the value
  2.1213 +    # when lists are switched off
  2.1214 +    _nolistvalue = re.compile(r'''^
  2.1215 +        (
  2.1216 +            (?:".*?")|          # double quotes
  2.1217 +            (?:'.*?')|          # single quotes
  2.1218 +            (?:[^'"\#].*?)|     # unquoted
  2.1219 +            (?:)                # Empty value
  2.1220 +        )
  2.1221 +        \s*(\#.*)?              # optional comment
  2.1222 +        $''',
  2.1223 +        re.VERBOSE)
  2.1224 +
  2.1225 +    # regexes for finding triple quoted values on one line
  2.1226 +    _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
  2.1227 +    _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
  2.1228 +    _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
  2.1229 +    _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
  2.1230 +
  2.1231 +    _triple_quote = {
  2.1232 +        "'''": (_single_line_single, _multi_line_single),
  2.1233 +        '"""': (_single_line_double, _multi_line_double),
  2.1234 +    }
  2.1235 +
  2.1236 +    # Used by the ``istrue`` Section method
  2.1237 +    _bools = {
  2.1238 +        'yes': True, 'no': False,
  2.1239 +        'on': True, 'off': False,
  2.1240 +        '1': True, '0': False,
  2.1241 +        'true': True, 'false': False,
  2.1242 +        }
  2.1243 +
  2.1244 +
  2.1245 +    def __init__(self, infile=None, options=None, **kwargs):
  2.1246 +        """
  2.1247 +        Parse a config file or create a config file object.
  2.1248 +        
  2.1249 +        ``ConfigObj(infile=None, options=None, **kwargs)``
  2.1250 +        """
  2.1251 +        # init the superclass
  2.1252 +        Section.__init__(self, self, 0, self)
  2.1253 +        
  2.1254 +        if infile is None:
  2.1255 +            infile = []
  2.1256 +        if options is None:
  2.1257 +            options = {}
  2.1258 +        else:
  2.1259 +            options = dict(options)
  2.1260 +            
  2.1261 +        # keyword arguments take precedence over an options dictionary
  2.1262 +        options.update(kwargs)
  2.1263 +        
  2.1264 +        defaults = OPTION_DEFAULTS.copy()
  2.1265 +        # TODO: check the values too.
  2.1266 +        for entry in options:
  2.1267 +            if entry not in defaults:
  2.1268 +                raise TypeError('Unrecognised option "%s".' % entry)
  2.1269 +        
  2.1270 +        # Add any explicit options to the defaults
  2.1271 +        defaults.update(options)
  2.1272 +        self._initialise(defaults)
  2.1273 +        configspec = defaults['configspec']
  2.1274 +        self._original_configspec = configspec
  2.1275 +        self._load(infile, configspec)
  2.1276 +        
  2.1277 +        
  2.1278 +    def _load(self, infile, configspec):
  2.1279 +        if isinstance(infile, StringTypes):
  2.1280 +            self.filename = infile
  2.1281 +            if os.path.isfile(infile):
  2.1282 +                h = open(infile, 'rb')
  2.1283 +                infile = h.read() or []
  2.1284 +                h.close()
  2.1285 +            elif self.file_error:
  2.1286 +                # raise an error if the file doesn't exist
  2.1287 +                raise IOError('Config file not found: "%s".' % self.filename)
  2.1288 +            else:
  2.1289 +                # file doesn't already exist
  2.1290 +                if self.create_empty:
  2.1291 +                    # this is a good test that the filename specified
  2.1292 +                    # isn't impossible - like on a non-existent device
  2.1293 +                    h = open(infile, 'w')
  2.1294 +                    h.write('')
  2.1295 +                    h.close()
  2.1296 +                infile = []
  2.1297 +                
  2.1298 +        elif isinstance(infile, (list, tuple)):
  2.1299 +            infile = list(infile)
  2.1300 +            
  2.1301 +        elif isinstance(infile, dict):
  2.1302 +            # initialise self
  2.1303 +            # the Section class handles creating subsections
  2.1304 +            if isinstance(infile, ConfigObj):
  2.1305 +                # get a copy of our ConfigObj
  2.1306 +                infile = infile.dict()
  2.1307 +                
  2.1308 +            for entry in infile:
  2.1309 +                self[entry] = infile[entry]
  2.1310 +            del self._errors
  2.1311 +            
  2.1312 +            if configspec is not None:
  2.1313 +                self._handle_configspec(configspec)
  2.1314 +            else:
  2.1315 +                self.configspec = None
  2.1316 +            return
  2.1317 +        
  2.1318 +        elif hasattr(infile, 'read'):
  2.1319 +            # This supports file like objects
  2.1320 +            infile = infile.read() or []
  2.1321 +            # needs splitting into lines - but needs doing *after* decoding
  2.1322 +            # in case it's not an 8 bit encoding
  2.1323 +        else:
  2.1324 +            raise TypeError('infile must be a filename, file like object, or list of lines.')
  2.1325 +        
  2.1326 +        if infile:
  2.1327 +            # don't do it for the empty ConfigObj
  2.1328 +            infile = self._handle_bom(infile)
  2.1329 +            # infile is now *always* a list
  2.1330 +            #
  2.1331 +            # Set the newlines attribute (first line ending it finds)
  2.1332 +            # and strip trailing '\n' or '\r' from lines
  2.1333 +            for line in infile:
  2.1334 +                if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
  2.1335 +                    continue
  2.1336 +                for end in ('\r\n', '\n', '\r'):
  2.1337 +                    if line.endswith(end):
  2.1338 +                        self.newlines = end
  2.1339 +                        break
  2.1340 +                break
  2.1341 +
  2.1342 +            infile = [line.rstrip('\r\n') for line in infile]
  2.1343 +            
  2.1344 +        self._parse(infile)
  2.1345 +        # if we had any errors, now is the time to raise them
  2.1346 +        if self._errors:
  2.1347 +            info = "at line %s." % self._errors[0].line_number
  2.1348 +            if len(self._errors) > 1:
  2.1349 +                msg = "Parsing failed with several errors.\nFirst error %s" % info
  2.1350 +                error = ConfigObjError(msg)
  2.1351 +            else:
  2.1352 +                error = self._errors[0]
  2.1353 +            # set the errors attribute; it's a list of tuples:
  2.1354 +            # (error_type, message, line_number)
  2.1355 +            error.errors = self._errors
  2.1356 +            # set the config attribute
  2.1357 +            error.config = self
  2.1358 +            raise error
  2.1359 +        # delete private attributes
  2.1360 +        del self._errors
  2.1361 +        
  2.1362 +        if configspec is None:
  2.1363 +            self.configspec = None
  2.1364 +        else:
  2.1365 +            self._handle_configspec(configspec)
  2.1366 +    
  2.1367 +    
  2.1368 +    def _initialise(self, options=None):
  2.1369 +        if options is None:
  2.1370 +            options = OPTION_DEFAULTS
  2.1371 +            
  2.1372 +        # initialise a few variables
  2.1373 +        self.filename = None
  2.1374 +        self._errors = []
  2.1375 +        self.raise_errors = options['raise_errors']
  2.1376 +        self.interpolation = options['interpolation']
  2.1377 +        self.list_values = options['list_values']
  2.1378 +        self.create_empty = options['create_empty']
  2.1379 +        self.file_error = options['file_error']
  2.1380 +        self.stringify = options['stringify']
  2.1381 +        self.indent_type = options['indent_type']
  2.1382 +        self.encoding = options['encoding']
  2.1383 +        self.default_encoding = options['default_encoding']
  2.1384 +        self.BOM = False
  2.1385 +        self.newlines = None
  2.1386 +        self.write_empty_values = options['write_empty_values']
  2.1387 +        self.unrepr = options['unrepr']
  2.1388 +        
  2.1389 +        self.initial_comment = []
  2.1390 +        self.final_comment = []
  2.1391 +        self.configspec = {}
  2.1392 +        
  2.1393 +        # Clear section attributes as well
  2.1394 +        Section._initialise(self)
  2.1395 +        
  2.1396 +        
  2.1397 +    def __repr__(self):
  2.1398 +        return ('ConfigObj({%s})' % 
  2.1399 +                ', '.join([('%s: %s' % (repr(key), repr(self[key]))) 
  2.1400 +                for key in (self.scalars + self.sections)]))
  2.1401 +    
  2.1402 +    
  2.1403 +    def _handle_bom(self, infile):
  2.1404 +        """
  2.1405 +        Handle any BOM, and decode if necessary.
  2.1406 +        
  2.1407 +        If an encoding is specified, that *must* be used - but the BOM should
  2.1408 +        still be removed (and the BOM attribute set).
  2.1409 +        
  2.1410 +        (If the encoding is wrongly specified, then a BOM for an alternative
  2.1411 +        encoding won't be discovered or removed.)
  2.1412 +        
  2.1413 +        If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
  2.1414 +        removed. The BOM attribute will be set. UTF16 will be decoded to
  2.1415 +        unicode.
  2.1416 +        
  2.1417 +        NOTE: This method must not be called with an empty ``infile``.
  2.1418 +        
  2.1419 +        Specifying the *wrong* encoding is likely to cause a
  2.1420 +        ``UnicodeDecodeError``.
  2.1421 +        
  2.1422 +        ``infile`` must always be returned as a list of lines, but may be
  2.1423 +        passed in as a single string.
  2.1424 +        """
  2.1425 +        if ((self.encoding is not None) and
  2.1426 +            (self.encoding.lower() not in BOM_LIST)):
  2.1427 +            # No need to check for a BOM
  2.1428 +            # the encoding specified doesn't have one
  2.1429 +            # just decode
  2.1430 +            return self._decode(infile, self.encoding)
  2.1431 +        
  2.1432 +        if isinstance(infile, (list, tuple)):
  2.1433 +            line = infile[0]
  2.1434 +        else:
  2.1435 +            line = infile
  2.1436 +        if self.encoding is not None:
  2.1437 +            # encoding explicitly supplied
  2.1438 +            # And it could have an associated BOM
  2.1439 +            # TODO: if encoding is just UTF16 - we ought to check for both
  2.1440 +            # TODO: big endian and little endian versions.
  2.1441 +            enc = BOM_LIST[self.encoding.lower()]
  2.1442 +            if enc == 'utf_16':
  2.1443 +                # For UTF16 we try big endian and little endian
  2.1444 +                for BOM, (encoding, final_encoding) in BOMS.items():
  2.1445 +                    if not final_encoding:
  2.1446 +                        # skip UTF8
  2.1447 +                        continue
  2.1448 +                    if infile.startswith(BOM):
  2.1449 +                        ### BOM discovered
  2.1450 +                        ##self.BOM = True
  2.1451 +                        # Don't need to remove BOM
  2.1452 +                        return self._decode(infile, encoding)
  2.1453 +                    
  2.1454 +                # If we get this far, will *probably* raise a DecodeError
  2.1455 +                # As it doesn't appear to start with a BOM
  2.1456 +                return self._decode(infile, self.encoding)
  2.1457 +            
  2.1458 +            # Must be UTF8
  2.1459 +            BOM = BOM_SET[enc]
  2.1460 +            if not line.startswith(BOM):
  2.1461 +                return self._decode(infile, self.encoding)
  2.1462 +            
  2.1463 +            newline = line[len(BOM):]
  2.1464 +            
  2.1465 +            # BOM removed
  2.1466 +            if isinstance(infile, (list, tuple)):
  2.1467 +                infile[0] = newline
  2.1468 +            else:
  2.1469 +                infile = newline
  2.1470 +            self.BOM = True
  2.1471 +            return self._decode(infile, self.encoding)
  2.1472 +        
  2.1473 +        # No encoding specified - so we need to check for UTF8/UTF16
  2.1474 +        for BOM, (encoding, final_encoding) in BOMS.items():
  2.1475 +            if not line.startswith(BOM):
  2.1476 +                continue
  2.1477 +            else:
  2.1478 +                # BOM discovered
  2.1479 +                self.encoding = final_encoding
  2.1480 +                if not final_encoding:
  2.1481 +                    self.BOM = True
  2.1482 +                    # UTF8
  2.1483 +                    # remove BOM
  2.1484 +                    newline = line[len(BOM):]
  2.1485 +                    if isinstance(infile, (list, tuple)):
  2.1486 +                        infile[0] = newline
  2.1487 +                    else:
  2.1488 +                        infile = newline
  2.1489 +                    # UTF8 - don't decode
  2.1490 +                    if isinstance(infile, StringTypes):
  2.1491 +                        return infile.splitlines(True)
  2.1492 +                    else:
  2.1493 +                        return infile
  2.1494 +                # UTF16 - have to decode
  2.1495 +                return self._decode(infile, encoding)
  2.1496 +            
  2.1497 +        # No BOM discovered and no encoding specified, just return
  2.1498 +        if isinstance(infile, StringTypes):
  2.1499 +            # infile read from a file will be a single string
  2.1500 +            return infile.splitlines(True)
  2.1501 +        return infile
  2.1502 +
  2.1503 +
  2.1504 +    def _a_to_u(self, aString):
  2.1505 +        """Decode ASCII strings to unicode if a self.encoding is specified."""
  2.1506 +        if self.encoding:
  2.1507 +            return aString.decode('ascii')
  2.1508 +        else:
  2.1509 +            return aString
  2.1510 +
  2.1511 +
  2.1512 +    def _decode(self, infile, encoding):
  2.1513 +        """
  2.1514 +        Decode infile to unicode. Using the specified encoding.
  2.1515 +        
  2.1516 +        if is a string, it also needs converting to a list.
  2.1517 +        """
  2.1518 +        if isinstance(infile, StringTypes):
  2.1519 +            # can't be unicode
  2.1520 +            # NOTE: Could raise a ``UnicodeDecodeError``
  2.1521 +            return infile.decode(encoding).splitlines(True)
  2.1522 +        for i, line in enumerate(infile):
  2.1523 +            if not isinstance(line, unicode):
  2.1524 +                # NOTE: The isinstance test here handles mixed lists of unicode/string
  2.1525 +                # NOTE: But the decode will break on any non-string values
  2.1526 +                # NOTE: Or could raise a ``UnicodeDecodeError``
  2.1527 +                infile[i] = line.decode(encoding)
  2.1528 +        return infile
  2.1529 +
  2.1530 +
  2.1531 +    def _decode_element(self, line):
  2.1532 +        """Decode element to unicode if necessary."""
  2.1533 +        if not self.encoding:
  2.1534 +            return line
  2.1535 +        if isinstance(line, str) and self.default_encoding:
  2.1536 +            return line.decode(self.default_encoding)
  2.1537 +        return line
  2.1538 +
  2.1539 +
  2.1540 +    def _str(self, value):
  2.1541 +        """
  2.1542 +        Used by ``stringify`` within validate, to turn non-string values
  2.1543 +        into strings.
  2.1544 +        """
  2.1545 +        if not isinstance(value, StringTypes):
  2.1546 +            return str(value)
  2.1547 +        else:
  2.1548 +            return value
  2.1549 +
  2.1550 +
  2.1551 +    def _parse(self, infile):
  2.1552 +        """Actually parse the config file."""
  2.1553 +        temp_list_values = self.list_values
  2.1554 +        if self.unrepr:
  2.1555 +            self.list_values = False
  2.1556 +            
  2.1557 +        comment_list = []
  2.1558 +        done_start = False
  2.1559 +        this_section = self
  2.1560 +        maxline = len(infile) - 1
  2.1561 +        cur_index = -1
  2.1562 +        reset_comment = False
  2.1563 +        
  2.1564 +        while cur_index < maxline:
  2.1565 +            if reset_comment:
  2.1566 +                comment_list = []
  2.1567 +            cur_index += 1
  2.1568 +            line = infile[cur_index]
  2.1569 +            sline = line.strip()
  2.1570 +            # do we have anything on the line ?
  2.1571 +            if not sline or sline.startswith('#'):
  2.1572 +                reset_comment = False
  2.1573 +                comment_list.append(line)
  2.1574 +                continue
  2.1575 +            
  2.1576 +            if not done_start:
  2.1577 +                # preserve initial comment
  2.1578 +                self.initial_comment = comment_list
  2.1579 +                comment_list = []
  2.1580 +                done_start = True
  2.1581 +                
  2.1582 +            reset_comment = True
  2.1583 +            # first we check if it's a section marker
  2.1584 +            mat = self._sectionmarker.match(line)
  2.1585 +            if mat is not None:
  2.1586 +                # is a section line
  2.1587 +                (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
  2.1588 +                if indent and (self.indent_type is None):
  2.1589 +                    self.indent_type = indent
  2.1590 +                cur_depth = sect_open.count('[')
  2.1591 +                if cur_depth != sect_close.count(']'):
  2.1592 +                    self._handle_error("Cannot compute the section depth at line %s.",
  2.1593 +                                       NestingError, infile, cur_index)
  2.1594 +                    continue
  2.1595 +                
  2.1596 +                if cur_depth < this_section.depth:
  2.1597 +                    # the new section is dropping back to a previous level
  2.1598 +                    try:
  2.1599 +                        parent = self._match_depth(this_section,
  2.1600 +                                                   cur_depth).parent
  2.1601 +                    except SyntaxError:
  2.1602 +                        self._handle_error("Cannot compute nesting level at line %s.",
  2.1603 +                                           NestingError, infile, cur_index)
  2.1604 +                        continue
  2.1605 +                elif cur_depth == this_section.depth:
  2.1606 +                    # the new section is a sibling of the current section
  2.1607 +                    parent = this_section.parent
  2.1608 +                elif cur_depth == this_section.depth + 1:
  2.1609 +                    # the new section is a child the current section
  2.1610 +                    parent = this_section
  2.1611 +                else:
  2.1612 +                    self._handle_error("Section too nested at line %s.",
  2.1613 +                                       NestingError, infile, cur_index)
  2.1614 +                    
  2.1615 +                sect_name = self._unquote(sect_name)
  2.1616 +                if parent.has_key(sect_name):
  2.1617 +                    self._handle_error('Duplicate section name at line %s.',
  2.1618 +                                       DuplicateError, infile, cur_index)
  2.1619 +                    continue
  2.1620 +                
  2.1621 +                # create the new section
  2.1622 +                this_section = Section(
  2.1623 +                    parent,
  2.1624 +                    cur_depth,
  2.1625 +                    self,
  2.1626 +                    name=sect_name)
  2.1627 +                parent[sect_name] = this_section
  2.1628 +                parent.inline_comments[sect_name] = comment
  2.1629 +                parent.comments[sect_name] = comment_list
  2.1630 +                continue
  2.1631 +            #
  2.1632 +            # it's not a section marker,
  2.1633 +            # so it should be a valid ``key = value`` line
  2.1634 +            mat = self._keyword.match(line)
  2.1635 +            if mat is None:
  2.1636 +                # it neither matched as a keyword
  2.1637 +                # or a section marker
  2.1638 +                self._handle_error(
  2.1639 +                    'Invalid line at line "%s".',
  2.1640 +                    ParseError, infile, cur_index)
  2.1641 +            else:
  2.1642 +                # is a keyword value
  2.1643 +                # value will include any inline comment
  2.1644 +                (indent, key, value) = mat.groups()
  2.1645 +                if indent and (self.indent_type is None):
  2.1646 +                    self.indent_type = indent
  2.1647 +                # check for a multiline value
  2.1648 +                if value[:3] in ['"""', "'''"]:
  2.1649 +                    try:
  2.1650 +                        (value, comment, cur_index) = self._multiline(
  2.1651 +                            value, infile, cur_index, maxline)
  2.1652 +                    except SyntaxError:
  2.1653 +                        self._handle_error(
  2.1654 +                            'Parse error in value at line %s.',
  2.1655 +                            ParseError, infile, cur_index)
  2.1656 +                        continue
  2.1657 +                    else:
  2.1658 +                        if self.unrepr:
  2.1659 +                            comment = ''
  2.1660 +                            try:
  2.1661 +                                value = unrepr(value)
  2.1662 +                            except Exception, e:
  2.1663 +                                if type(e) == UnknownType:
  2.1664 +                                    msg = 'Unknown name or type in value at line %s.'
  2.1665 +                                else:
  2.1666 +                                    msg = 'Parse error in value at line %s.'
  2.1667 +                                self._handle_error(msg, UnreprError, infile,
  2.1668 +                                    cur_index)
  2.1669 +                                continue
  2.1670 +                else:
  2.1671 +                    if self.unrepr:
  2.1672 +                        comment = ''
  2.1673 +                        try:
  2.1674 +                            value = unrepr(value)
  2.1675 +                        except Exception, e:
  2.1676 +                            if isinstance(e, UnknownType):
  2.1677 +                                msg = 'Unknown name or type in value at line %s.'
  2.1678 +                            else:
  2.1679 +                                msg = 'Parse error in value at line %s.'
  2.1680 +                            self._handle_error(msg, UnreprError, infile,
  2.1681 +                                cur_index)
  2.1682 +                            continue
  2.1683 +                    else:
  2.1684 +                        # extract comment and lists
  2.1685 +                        try:
  2.1686 +                            (value, comment) = self._handle_value(value)
  2.1687 +                        except SyntaxError:
  2.1688 +                            self._handle_error(
  2.1689 +                                'Parse error in value at line %s.',
  2.1690 +                                ParseError, infile, cur_index)
  2.1691 +                            continue
  2.1692 +                #
  2.1693 +                key = self._unquote(key)
  2.1694 +                if this_section.has_key(key):
  2.1695 +                    self._handle_error(
  2.1696 +                        'Duplicate keyword name at line %s.',
  2.1697 +                        DuplicateError, infile, cur_index)
  2.1698 +                    continue
  2.1699 +                # add the key.
  2.1700 +                # we set unrepr because if we have got this far we will never
  2.1701 +                # be creating a new section
  2.1702 +                this_section.__setitem__(key, value, unrepr=True)
  2.1703 +                this_section.inline_comments[key] = comment
  2.1704 +                this_section.comments[key] = comment_list
  2.1705 +                continue
  2.1706 +        #
  2.1707 +        if self.indent_type is None:
  2.1708 +            # no indentation used, set the type accordingly
  2.1709 +            self.indent_type = ''
  2.1710 +
  2.1711 +        # preserve the final comment
  2.1712 +        if not self and not self.initial_comment:
  2.1713 +            self.initial_comment = comment_list
  2.1714 +        elif not reset_comment:
  2.1715 +            self.final_comment = comment_list
  2.1716 +        self.list_values = temp_list_values
  2.1717 +
  2.1718 +
  2.1719 +    def _match_depth(self, sect, depth):
  2.1720 +        """
  2.1721 +        Given a section and a depth level, walk back through the sections
  2.1722 +        parents to see if the depth level matches a previous section.
  2.1723 +        
  2.1724 +        Return a reference to the right section,
  2.1725 +        or raise a SyntaxError.
  2.1726 +        """
  2.1727 +        while depth < sect.depth:
  2.1728 +            if sect is sect.parent:
  2.1729 +                # we've reached the top level already
  2.1730 +                raise SyntaxError()
  2.1731 +            sect = sect.parent
  2.1732 +        if sect.depth == depth:
  2.1733 +            return sect
  2.1734 +        # shouldn't get here
  2.1735 +        raise SyntaxError()
  2.1736 +
  2.1737 +
  2.1738 +    def _handle_error(self, text, ErrorClass, infile, cur_index):
  2.1739 +        """
  2.1740 +        Handle an error according to the error settings.
  2.1741 +        
  2.1742 +        Either raise the error or store it.
  2.1743 +        The error will have occured at ``cur_index``
  2.1744 +        """
  2.1745 +        line = infile[cur_index]
  2.1746 +        cur_index += 1
  2.1747 +        message = text % cur_index
  2.1748 +        error = ErrorClass(message, cur_index, line)
  2.1749 +        if self.raise_errors:
  2.1750 +            # raise the error - parsing stops here
  2.1751 +            raise error
  2.1752 +        # store the error
  2.1753 +        # reraise when parsing has finished
  2.1754 +        self._errors.append(error)
  2.1755 +
  2.1756 +
  2.1757 +    def _unquote(self, value):
  2.1758 +        """Return an unquoted version of a value"""
  2.1759 +        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
  2.1760 +            value = value[1:-1]
  2.1761 +        return value
  2.1762 +
  2.1763 +
  2.1764 +    def _quote(self, value, multiline=True):
  2.1765 +        """
  2.1766 +        Return a safely quoted version of a value.
  2.1767 +        
  2.1768 +        Raise a ConfigObjError if the value cannot be safely quoted.
  2.1769 +        If multiline is ``True`` (default) then use triple quotes
  2.1770 +        if necessary.
  2.1771 +        
  2.1772 +        Don't quote values that don't need it.
  2.1773 +        Recursively quote members of a list and return a comma joined list.
  2.1774 +        Multiline is ``False`` for lists.
  2.1775 +        Obey list syntax for empty and single member lists.
  2.1776 +        
  2.1777 +        If ``list_values=False`` then the value is only quoted if it contains
  2.1778 +        a ``\n`` (is multiline) or '#'.
  2.1779 +        
  2.1780 +        If ``write_empty_values`` is set, and the value is an empty string, it
  2.1781 +        won't be quoted.
  2.1782 +        """
  2.1783 +        if multiline and self.write_empty_values and value == '':
  2.1784 +            # Only if multiline is set, so that it is used for values not
  2.1785 +            # keys, and not values that are part of a list
  2.1786 +            return ''
  2.1787 +        
  2.1788 +        if multiline and isinstance(value, (list, tuple)):
  2.1789 +            if not value:
  2.1790 +                return ','
  2.1791 +            elif len(value) == 1:
  2.1792 +                return self._quote(value[0], multiline=False) + ','
  2.1793 +            return ', '.join([self._quote(val, multiline=False)
  2.1794 +                for val in value])
  2.1795 +        if not isinstance(value, StringTypes):
  2.1796 +            if self.stringify:
  2.1797 +                value = str(value)
  2.1798 +            else:
  2.1799 +                raise TypeError('Value "%s" is not a string.' % value)
  2.1800 +
  2.1801 +        if not value:
  2.1802 +            return '""'
  2.1803 +        
  2.1804 +        no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
  2.1805 +        need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
  2.1806 +        hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
  2.1807 +        check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
  2.1808 +        
  2.1809 +        if check_for_single:
  2.1810 +            if not self.list_values:
  2.1811 +                # we don't quote if ``list_values=False``
  2.1812 +                quot = noquot
  2.1813 +            # for normal values either single or double quotes will do
  2.1814 +            elif '\n' in value:
  2.1815 +                # will only happen if multiline is off - e.g. '\n' in key
  2.1816 +                raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
  2.1817 +            elif ((value[0] not in wspace_plus) and
  2.1818 +                    (value[-1] not in wspace_plus) and
  2.1819 +                    (',' not in value)):
  2.1820 +                quot = noquot
  2.1821 +            else:
  2.1822 +                quot = self._get_single_quote(value)
  2.1823 +        else:
  2.1824 +            # if value has '\n' or "'" *and* '"', it will need triple quotes
  2.1825 +            quot = self._get_triple_quote(value)
  2.1826 +        
  2.1827 +        if quot == noquot and '#' in value and self.list_values:
  2.1828 +            quot = self._get_single_quote(value)
  2.1829 +                
  2.1830 +        return quot % value
  2.1831 +    
  2.1832 +    
  2.1833 +    def _get_single_quote(self, value):
  2.1834 +        if ("'" in value) and ('"' in value):
  2.1835 +            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
  2.1836 +        elif '"' in value:
  2.1837 +            quot = squot
  2.1838 +        else:
  2.1839 +            quot = dquot
  2.1840 +        return quot
  2.1841 +    
  2.1842 +    
  2.1843 +    def _get_triple_quote(self, value):
  2.1844 +        if (value.find('"""') != -1) and (value.find("'''") != -1):
  2.1845 +            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
  2.1846 +        if value.find('"""') == -1:
  2.1847 +            quot = tdquot
  2.1848 +        else:
  2.1849 +            quot = tsquot 
  2.1850 +        return quot
  2.1851 +
  2.1852 +
  2.1853 +    def _handle_value(self, value):
  2.1854 +        """
  2.1855 +        Given a value string, unquote, remove comment,
  2.1856 +        handle lists. (including empty and single member lists)
  2.1857 +        """
  2.1858 +        # do we look for lists in values ?
  2.1859 +        if not self.list_values:
  2.1860 +            mat = self._nolistvalue.match(value)
  2.1861 +            if mat is None:
  2.1862 +                raise SyntaxError()
  2.1863 +            # NOTE: we don't unquote here
  2.1864 +            return mat.groups()
  2.1865 +        #
  2.1866 +        mat = self._valueexp.match(value)
  2.1867 +        if mat is None:
  2.1868 +            # the value is badly constructed, probably badly quoted,
  2.1869 +            # or an invalid list
  2.1870 +            raise SyntaxError()
  2.1871 +        (list_values, single, empty_list, comment) = mat.groups()
  2.1872 +        if (list_values == '') and (single is None):
  2.1873 +            # change this if you want to accept empty values
  2.1874 +            raise SyntaxError()
  2.1875 +        # NOTE: note there is no error handling from here if the regex
  2.1876 +        # is wrong: then incorrect values will slip through
  2.1877 +        if empty_list is not None:
  2.1878 +            # the single comma - meaning an empty list
  2.1879 +            return ([], comment)
  2.1880 +        if single is not None:
  2.1881 +            # handle empty values
  2.1882 +            if list_values and not single:
  2.1883 +                # FIXME: the '' is a workaround because our regex now matches
  2.1884 +                #   '' at the end of a list if it has a trailing comma
  2.1885 +                single = None
  2.1886 +            else:
  2.1887 +                single = single or '""'
  2.1888 +                single = self._unquote(single)
  2.1889 +        if list_values == '':
  2.1890 +            # not a list value
  2.1891 +            return (single, comment)
  2.1892 +        the_list = self._listvalueexp.findall(list_values)
  2.1893 +        the_list = [self._unquote(val) for val in the_list]
  2.1894 +        if single is not None:
  2.1895 +            the_list += [single]
  2.1896 +        return (the_list, comment)
  2.1897 +
  2.1898 +
  2.1899 +    def _multiline(self, value, infile, cur_index, maxline):
  2.1900 +        """Extract the value, where we are in a multiline situation."""
  2.1901 +        quot = value[:3]
  2.1902 +        newvalue = value[3:]
  2.1903 +        single_line = self._triple_quote[quot][0]
  2.1904 +        multi_line = self._triple_quote[quot][1]
  2.1905 +        mat = single_line.match(value)
  2.1906 +        if mat is not None:
  2.1907 +            retval = list(mat.groups())
  2.1908 +            retval.append(cur_index)
  2.1909 +            return retval
  2.1910 +        elif newvalue.find(quot) != -1:
  2.1911 +            # somehow the triple quote is missing
  2.1912 +            raise SyntaxError()
  2.1913 +        #
  2.1914 +        while cur_index < maxline:
  2.1915 +            cur_index += 1
  2.1916 +            newvalue += '\n'
  2.1917 +            line = infile[cur_index]
  2.1918 +            if line.find(quot) == -1:
  2.1919 +                newvalue += line
  2.1920 +            else:
  2.1921 +                # end of multiline, process it
  2.1922 +                break
  2.1923 +        else:
  2.1924 +            # we've got to the end of the config, oops...
  2.1925 +            raise SyntaxError()
  2.1926 +        mat = multi_line.match(line)
  2.1927 +        if mat is None:
  2.1928 +            # a badly formed line
  2.1929 +            raise SyntaxError()
  2.1930 +        (value, comment) = mat.groups()
  2.1931 +        return (newvalue + value, comment, cur_index)
  2.1932 +
  2.1933 +
  2.1934 +    def _handle_configspec(self, configspec):
  2.1935 +        """Parse the configspec."""
  2.1936 +        # FIXME: Should we check that the configspec was created with the 
  2.1937 +        #        correct settings ? (i.e. ``list_values=False``)
  2.1938 +        if not isinstance(configspec, ConfigObj):
  2.1939 +            try:
  2.1940 +                configspec = ConfigObj(configspec,
  2.1941 +                                       raise_errors=True,
  2.1942 +                                       file_error=True,
  2.1943 +                                       list_values=False)
  2.1944 +            except ConfigObjError, e:
  2.1945 +                # FIXME: Should these errors have a reference
  2.1946 +                #        to the already parsed ConfigObj ?
  2.1947 +                raise ConfigspecError('Parsing configspec failed: %s' % e)
  2.1948 +            except IOError, e:
  2.1949 +                raise IOError('Reading configspec failed: %s' % e)
  2.1950 +        
  2.1951 +        self._set_configspec_value(configspec, self)
  2.1952 +
  2.1953 +
  2.1954 +    def _set_configspec_value(self, configspec, section):
  2.1955 +        """Used to recursively set configspec values."""
  2.1956 +        if '__many__' in configspec.sections:
  2.1957 +            section.configspec['__many__'] = configspec['__many__']
  2.1958 +            if len(configspec.sections) > 1:
  2.1959 +                # FIXME: can we supply any useful information here ?
  2.1960 +                raise RepeatSectionError()
  2.1961 +            
  2.1962 +        if hasattr(configspec, 'initial_comment'):
  2.1963 +            section._configspec_initial_comment = configspec.initial_comment
  2.1964 +            section._configspec_final_comment = configspec.final_comment
  2.1965 +            section._configspec_encoding = configspec.encoding
  2.1966 +            section._configspec_BOM = configspec.BOM
  2.1967 +            section._configspec_newlines = configspec.newlines
  2.1968 +            section._configspec_indent_type = configspec.indent_type
  2.1969 +            
  2.1970 +        for entry in configspec.scalars:
  2.1971 +            section._configspec_comments[entry] = configspec.comments[entry]
  2.1972 +            section._configspec_inline_comments[entry] = configspec.inline_comments[entry]
  2.1973 +            section.configspec[entry] = configspec[entry]
  2.1974 +            section._order.append(entry)
  2.1975 +            
  2.1976 +        for entry in configspec.sections:
  2.1977 +            if entry == '__many__':
  2.1978 +                continue
  2.1979 +            
  2.1980 +            section._cs_section_comments[entry] = configspec.comments[entry]
  2.1981 +            section._cs_section_inline_comments[entry] = configspec.inline_comments[entry]
  2.1982 +            if not section.has_key(entry):
  2.1983 +                section[entry] = {}
  2.1984 +            self._set_configspec_value(configspec[entry], section[entry])
  2.1985 +
  2.1986 +
  2.1987 +    def _handle_repeat(self, section, configspec):
  2.1988 +        """Dynamically assign configspec for repeated section."""
  2.1989 +        try:
  2.1990 +            section_keys = configspec.sections
  2.1991 +            scalar_keys = configspec.scalars
  2.1992 +        except AttributeError:
  2.1993 +            section_keys = [entry for entry in configspec 
  2.1994 +                                if isinstance(configspec[entry], dict)]
  2.1995 +            scalar_keys = [entry for entry in configspec 
  2.1996 +                                if not isinstance(configspec[entry], dict)]
  2.1997 +            
  2.1998 +        if '__many__' in section_keys and len(section_keys) > 1:
  2.1999 +            # FIXME: can we supply any useful information here ?
  2.2000 +            raise RepeatSectionError()
  2.2001 +        
  2.2002 +        scalars = {}
  2.2003 +        sections = {}
  2.2004 +        for entry in scalar_keys:
  2.2005 +            val = configspec[entry]
  2.2006 +            scalars[entry] = val
  2.2007 +        for entry in section_keys:
  2.2008 +            val = configspec[entry]
  2.2009 +            if entry == '__many__':
  2.2010 +                scalars[entry] = val
  2.2011 +                continue
  2.2012 +            sections[entry] = val
  2.2013 +            
  2.2014 +        section.configspec = scalars
  2.2015 +        for entry in sections:
  2.2016 +            if not section.has_key(entry):
  2.2017 +                section[entry] = {}
  2.2018 +            self._handle_repeat(section[entry], sections[entry])
  2.2019 +
  2.2020 +
  2.2021 +    def _write_line(self, indent_string, entry, this_entry, comment):
  2.2022 +        """Write an individual line, for the write method"""
  2.2023 +        # NOTE: the calls to self._quote here handles non-StringType values.
  2.2024 +        if not self.unrepr:
  2.2025 +            val = self._decode_element(self._quote(this_entry))
  2.2026 +        else:
  2.2027 +            val = repr(this_entry)
  2.2028 +        return '%s%s%s%s%s' % (indent_string,
  2.2029 +                               self._decode_element(self._quote(entry, multiline=False)),
  2.2030 +                               self._a_to_u(' = '),
  2.2031 +                               val,
  2.2032 +                               self._decode_element(comment))
  2.2033 +
  2.2034 +
  2.2035 +    def _write_marker(self, indent_string, depth, entry, comment):
  2.2036 +        """Write a section marker line"""
  2.2037 +        return '%s%s%s%s%s' % (indent_string,
  2.2038 +                               self._a_to_u('[' * depth),
  2.2039 +                               self._quote(self._decode_element(entry), multiline=False),
  2.2040 +                               self._a_to_u(']' * depth),
  2.2041 +                               self._decode_element(comment))
  2.2042 +
  2.2043 +
  2.2044 +    def _handle_comment(self, comment):
  2.2045 +        """Deal with a comment."""
  2.2046 +        if not comment:
  2.2047 +            return ''
  2.2048 +        start = self.indent_type
  2.2049 +        if not comment.startswith('#'):
  2.2050 +            start += self._a_to_u(' # ')
  2.2051 +        return (start + comment)
  2.2052 +
  2.2053 +
  2.2054 +    # Public methods
  2.2055 +
  2.2056 +    def write(self, outfile=None, section=None):
  2.2057 +        """
  2.2058 +        Write the current ConfigObj as a file
  2.2059 +        
  2.2060 +        tekNico: FIXME: use StringIO instead of real files
  2.2061 +        
  2.2062 +        >>> filename = a.filename
  2.2063 +        >>> a.filename = 'test.ini'
  2.2064 +        >>> a.write()
  2.2065 +        >>> a.filename = filename
  2.2066 +        >>> a == ConfigObj('test.ini', raise_errors=True)
  2.2067 +        1
  2.2068 +        """
  2.2069 +        if self.indent_type is None:
  2.2070 +            # this can be true if initialised from a dictionary
  2.2071 +            self.indent_type = DEFAULT_INDENT_TYPE
  2.2072 +            
  2.2073 +        out = []
  2.2074 +        cs = self._a_to_u('#')
  2.2075 +        csp = self._a_to_u('# ')
  2.2076 +        if section is None:
  2.2077 +            int_val = self.interpolation
  2.2078 +            self.interpolation = False
  2.2079 +            section = self
  2.2080 +            for line in self.initial_comment:
  2.2081 +                line = self._decode_element(line)
  2.2082 +                stripped_line = line.strip()
  2.2083 +                if stripped_line and not stripped_line.startswith(cs):
  2.2084 +                    line = csp + line
  2.2085 +                out.append(line)
  2.2086 +                
  2.2087 +        indent_string = self.indent_type * section.depth
  2.2088 +        for entry in (section.scalars + section.sections):
  2.2089 +            if entry in section.defaults:
  2.2090 +                # don't write out default values
  2.2091 +                continue
  2.2092 +            for comment_line in section.comments[entry]:
  2.2093 +                comment_line = self._decode_element(comment_line.lstrip())
  2.2094 +                if comment_line and not comment_line.startswith(cs):
  2.2095 +                    comment_line = csp + comment_line
  2.2096 +                out.append(indent_string + comment_line)
  2.2097 +            this_entry = section[entry]
  2.2098 +            comment = self._handle_comment(section.inline_comments[entry])
  2.2099 +            
  2.2100 +            if isinstance(this_entry, dict):
  2.2101 +                # a section
  2.2102 +                out.append(self._write_marker(
  2.2103 +                    indent_string,
  2.2104 +                    this_entry.depth,
  2.2105 +                    entry,
  2.2106 +                    comment))
  2.2107 +                out.extend(self.write(section=this_entry))
  2.2108 +            else:
  2.2109 +                out.append(self._write_line(
  2.2110 +                    indent_string,
  2.2111 +                    entry,
  2.2112 +                    this_entry,
  2.2113 +                    comment))
  2.2114 +                
  2.2115 +        if section is self:
  2.2116 +            for line in self.final_comment:
  2.2117 +                line = self._decode_element(line)
  2.2118 +                stripped_line = line.strip()
  2.2119 +                if stripped_line and not stripped_line.startswith(cs):
  2.2120 +                    line = csp + line
  2.2121 +                out.append(line)
  2.2122 +            self.interpolation = int_val
  2.2123 +            
  2.2124 +        if section is not self:
  2.2125 +            return out
  2.2126 +        
  2.2127 +        if (self.filename is None) and (outfile is None):
  2.2128 +            # output a list of lines
  2.2129 +            # might need to encode
  2.2130 +            # NOTE: This will *screw* UTF16, each line will start with the BOM
  2.2131 +            if self.encoding:
  2.2132 +                out = [l.encode(self.encoding) for l in out]
  2.2133 +            if (self.BOM and ((self.encoding is None) or
  2.2134 +                (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
  2.2135 +                # Add the UTF8 BOM
  2.2136 +                if not out:
  2.2137 +                    out.append('')
  2.2138 +                out[0] = BOM_UTF8 + out[0]
  2.2139 +            return out
  2.2140 +        
  2.2141 +        # Turn the list to a string, joined with correct newlines
  2.2142 +        newline = self.newlines or os.linesep
  2.2143 +        output = self._a_to_u(newline).join(out)
  2.2144 +        if self.encoding:
  2.2145 +            output = output.encode(self.encoding)
  2.2146 +        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
  2.2147 +            # Add the UTF8 BOM
  2.2148 +            output = BOM_UTF8 + output
  2.2149 +            
  2.2150 +        if not output.endswith(newline):
  2.2151 +            output += newline
  2.2152 +        if outfile is not None:
  2.2153 +            outfile.write(output)
  2.2154 +        else:
  2.2155 +            h = open(self.filename, 'wb')
  2.2156 +            h.write(output)
  2.2157 +            h.close()
  2.2158 +
  2.2159 +
  2.2160 +    def validate(self, validator, preserve_errors=False, copy=False,
  2.2161 +                 section=None):
  2.2162 +        """
  2.2163 +        Test the ConfigObj against a configspec.
  2.2164 +        
  2.2165 +        It uses the ``validator`` object from *validate.py*.
  2.2166 +        
  2.2167 +        To run ``validate`` on the current ConfigObj, call: ::
  2.2168 +        
  2.2169 +            test = config.validate(validator)
  2.2170 +        
  2.2171 +        (Normally having previously passed in the configspec when the ConfigObj
  2.2172 +        was created - you can dynamically assign a dictionary of checks to the
  2.2173 +        ``configspec`` attribute of a section though).
  2.2174 +        
  2.2175 +        It returns ``True`` if everything passes, or a dictionary of
  2.2176 +        pass/fails (True/False). If every member of a subsection passes, it
  2.2177 +        will just have the value ``True``. (It also returns ``False`` if all
  2.2178 +        members fail).
  2.2179 +        
  2.2180 +        In addition, it converts the values from strings to their native
  2.2181 +        types if their checks pass (and ``stringify`` is set).
  2.2182 +        
  2.2183 +        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
  2.2184 +        of a marking a fail with a ``False``, it will preserve the actual
  2.2185 +        exception object. This can contain info about the reason for failure.
  2.2186 +        For example the ``VdtValueTooSmallError`` indicates that the value
  2.2187 +        supplied was too small. If a value (or section) is missing it will
  2.2188 +        still be marked as ``False``.
  2.2189 +        
  2.2190 +        You must have the validate module to use ``preserve_errors=True``.
  2.2191 +        
  2.2192 +        You can then use the ``flatten_errors`` function to turn your nested
  2.2193 +        results dictionary into a flattened list of failures - useful for
  2.2194 +        displaying meaningful error messages.
  2.2195 +        """
  2.2196 +        if section is None:
  2.2197 +            if self.configspec is None:
  2.2198 +                raise ValueError('No configspec supplied.')
  2.2199 +            if preserve_errors:
  2.2200 +                # We do this once to remove a top level dependency on the validate module
  2.2201 +                # Which makes importing configobj faster
  2.2202 +                from validate import VdtMissingValue
  2.2203 +                self._vdtMissingValue = VdtMissingValue
  2.2204 +            section = self
  2.2205 +        #
  2.2206 +        spec_section = section.configspec
  2.2207 +        if copy and hasattr(section, '_configspec_initial_comment'):
  2.2208 +            section.initial_comment = section._configspec_initial_comment
  2.2209 +            section.final_comment = section._configspec_final_comment
  2.2210 +            section.encoding = section._configspec_encoding
  2.2211 +            section.BOM = section._configspec_BOM
  2.2212 +            section.newlines = section._configspec_newlines
  2.2213 +            section.indent_type = section._configspec_indent_type
  2.2214 +            
  2.2215 +        if '__many__' in section.configspec:
  2.2216 +            many = spec_section['__many__']
  2.2217 +            # dynamically assign the configspecs
  2.2218 +            # for the sections below
  2.2219 +            for entry in section.sections:
  2.2220 +                self._handle_repeat(section[entry], many)
  2.2221 +        #
  2.2222 +        out = {}
  2.2223 +        ret_true = True
  2.2224 +        ret_false = True
  2.2225 +        order = [k for k in section._order if k in spec_section]
  2.2226 +        order += [k for k in spec_section if k not in order]
  2.2227 +        for entry in order:
  2.2228 +            if entry == '__many__':
  2.2229 +                continue
  2.2230 +            if (not entry in section.scalars) or (entry in section.defaults):
  2.2231 +                # missing entries
  2.2232 +                # or entries from defaults
  2.2233 +                missing = True
  2.2234 +                val = None
  2.2235 +                if copy and not entry in section.scalars:
  2.2236 +                    # copy comments
  2.2237 +                    section.comments[entry] = (
  2.2238 +                        section._configspec_comments.get(entry, []))
  2.2239 +                    section.inline_comments[entry] = (
  2.2240 +                        section._configspec_inline_comments.get(entry, ''))
  2.2241 +                #
  2.2242 +            else:
  2.2243 +                missing = False
  2.2244 +                val = section[entry]
  2.2245 +            try:
  2.2246 +                check = validator.check(spec_section[entry],
  2.2247 +                                        val,
  2.2248 +                                        missing=missing
  2.2249 +                                        )
  2.2250 +            except validator.baseErrorClass, e:
  2.2251 +                if not preserve_errors or isinstance(e, self._vdtMissingValue):
  2.2252 +                    out[entry] = False
  2.2253 +                else:
  2.2254 +                    # preserve the error
  2.2255 +                    out[entry] = e
  2.2256 +                    ret_false = False
  2.2257 +                ret_true = False
  2.2258 +            else:
  2.2259 +                try: 
  2.2260 +                    section.default_values.pop(entry, None)
  2.2261 +                except AttributeError: 
  2.2262 +                    # For Python 2.2 compatibility
  2.2263 +                    try:
  2.2264 +                        del section.default_values[entry]
  2.2265 +                    except KeyError:
  2.2266 +                        pass
  2.2267 +                    
  2.2268 +                if hasattr(validator, 'get_default_value'):
  2.2269 +                    try: 
  2.2270 +                        section.default_values[entry] = validator.get_default_value(spec_section[entry])
  2.2271 +                    except KeyError:
  2.2272 +                        # No default
  2.2273 +                        pass
  2.2274 +                    
  2.2275 +                ret_false = False
  2.2276 +                out[entry] = True
  2.2277 +                if self.stringify or missing:
  2.2278 +                    # if we are doing type conversion
  2.2279 +                    # or the value is a supplied default
  2.2280 +                    if not self.stringify:
  2.2281 +                        if isinstance(check, (list, tuple)):
  2.2282 +                            # preserve lists
  2.2283 +                            check = [self._str(item) for item in check]
  2.2284 +                        elif missing and check is None:
  2.2285 +                            # convert the None from a default to a ''
  2.2286 +                            check = ''
  2.2287 +                        else:
  2.2288 +                            check = self._str(check)
  2.2289 +                    if (check != val) or missing:
  2.2290 +                        section[entry] = check
  2.2291 +                if not copy and missing and entry not in section.defaults:
  2.2292 +                    section.defaults.append(entry)
  2.2293 +        # Missing sections will have been created as empty ones when the
  2.2294 +        # configspec was read.
  2.2295 +        for entry in section.sections:
  2.2296 +            # FIXME: this means DEFAULT is not copied in copy mode
  2.2297 +            if section is self and entry == 'DEFAULT':
  2.2298 +                continue
  2.2299 +            if copy:
  2.2300 +                section.comments[entry] = section._cs_section_comments.get(entry, [])
  2.2301 +                section.inline_comments[entry] = section._cs_section_inline_comments.get(entry, '')
  2.2302 +            check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
  2.2303 +            out[entry] = check
  2.2304 +            if check == False:
  2.2305 +                ret_true = False
  2.2306 +            elif check == True:
  2.2307 +                ret_false = False
  2.2308 +            else:
  2.2309 +                ret_true = False
  2.2310 +                ret_false = False
  2.2311 +        #
  2.2312 +        if ret_true:
  2.2313 +            return True
  2.2314 +        elif ret_false:
  2.2315 +            return False
  2.2316 +        return out
  2.2317 +
  2.2318 +
  2.2319 +    def reset(self):
  2.2320 +        """Clear ConfigObj instance and restore to 'freshly created' state."""
  2.2321 +        self.clear()
  2.2322 +        self._initialise()
  2.2323 +        # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
  2.2324 +        #        requires an empty dictionary
  2.2325 +        self.configspec = None
  2.2326 +        # Just to be sure ;-)
  2.2327 +        self._original_configspec = None
  2.2328 +        
  2.2329 +        
  2.2330 +    def reload(self):
  2.2331 +        """
  2.2332 +        Reload a ConfigObj from file.
  2.2333 +        
  2.2334 +        This method raises a ``ReloadError`` if the ConfigObj doesn't have
  2.2335 +        a filename attribute pointing to a file.
  2.2336 +        """
  2.2337 +        if not isinstance(self.filename, StringTypes):
  2.2338 +            raise ReloadError()
  2.2339 +
  2.2340 +        filename = self.filename
  2.2341 +        current_options = {}
  2.2342 +        for entry in OPTION_DEFAULTS:
  2.2343 +            if entry == 'configspec':
  2.2344 +                continue
  2.2345 +            current_options[entry] = getattr(self, entry)
  2.2346 +            
  2.2347 +        configspec = self._original_configspec
  2.2348 +        current_options['configspec'] = configspec
  2.2349 +            
  2.2350 +        self.clear()
  2.2351 +        self._initialise(current_options)
  2.2352 +        self._load(filename, configspec)
  2.2353 +        
  2.2354 +
  2.2355 +
  2.2356 +class SimpleVal(object):
  2.2357 +    """
  2.2358 +    A simple validator.
  2.2359 +    Can be used to check that all members expected are present.
  2.2360 +    
  2.2361 +    To use it, provide a configspec with all your members in (the value given
  2.2362 +    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
  2.2363 +    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
  2.2364 +    members are present, or a dictionary with True/False meaning
  2.2365 +    present/missing. (Whole missing sections will be replaced with ``False``)
  2.2366 +    """
  2.2367 +    
  2.2368 +    def __init__(self):
  2.2369 +        self.baseErrorClass = ConfigObjError
  2.2370 +    
  2.2371 +    def check(self, check, member, missing=False):
  2.2372 +        """A dummy check method, always returns the value unchanged."""
  2.2373 +        if missing:
  2.2374 +            raise self.baseErrorClass()
  2.2375 +        return member
  2.2376 +
  2.2377 +
  2.2378 +# Check / processing functions for options
  2.2379 +def flatten_errors(cfg, res, levels=None, results=None):
  2.2380 +    """
  2.2381 +    An example function that will turn a nested dictionary of results
  2.2382 +    (as returned by ``ConfigObj.validate``) into a flat list.
  2.2383 +    
  2.2384 +    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
  2.2385 +    dictionary returned by ``validate``.
  2.2386 +    
  2.2387 +    (This is a recursive function, so you shouldn't use the ``levels`` or
  2.2388 +    ``results`` arguments - they are used by the function.
  2.2389 +    
  2.2390 +    Returns a list of keys that failed. Each member of the list is a tuple :
  2.2391 +    ::
  2.2392 +    
  2.2393 +        ([list of sections...], key, result)
  2.2394 +    
  2.2395 +    If ``validate`` was called with ``preserve_errors=False`` (the default)
  2.2396 +    then ``result`` will always be ``False``.
  2.2397 +
  2.2398 +    *list of sections* is a flattened list of sections that the key was found
  2.2399 +    in.
  2.2400 +    
  2.2401 +    If the section was missing then key will be ``None``.
  2.2402 +    
  2.2403 +    If the value (or section) was missing then ``result`` will be ``False``.
  2.2404 +    
  2.2405 +    If ``validate`` was called with ``preserve_errors=True`` and a value
  2.2406 +    was present, but failed the check, then ``result`` will be the exception
  2.2407 +    object returned. You can use this as a string that describes the failure.
  2.2408 +    
  2.2409 +    For example *The value "3" is of the wrong type*.
  2.2410 +    
  2.2411 +    >>> import validate
  2.2412 +    >>> vtor = validate.Validator()
  2.2413 +    >>> my_ini = '''
  2.2414 +    ...     option1 = True
  2.2415 +    ...     [section1]
  2.2416 +    ...     option1 = True
  2.2417 +    ...     [section2]
  2.2418 +    ...     another_option = Probably
  2.2419 +    ...     [section3]
  2.2420 +    ...     another_option = True
  2.2421 +    ...     [[section3b]]
  2.2422 +    ...     value = 3
  2.2423 +    ...     value2 = a
  2.2424 +    ...     value3 = 11
  2.2425 +    ...     '''
  2.2426 +    >>> my_cfg = '''
  2.2427 +    ...     option1 = boolean()
  2.2428 +    ...     option2 = boolean()
  2.2429 +    ...     option3 = boolean(default=Bad_value)
  2.2430 +    ...     [section1]
  2.2431 +    ...     option1 = boolean()
  2.2432 +    ...     option2 = boolean()
  2.2433 +    ...     option3 = boolean(default=Bad_value)
  2.2434 +    ...     [section2]
  2.2435 +    ...     another_option = boolean()
  2.2436 +    ...     [section3]
  2.2437 +    ...     another_option = boolean()
  2.2438 +    ...     [[section3b]]
  2.2439 +    ...     value = integer
  2.2440 +    ...     value2 = integer
  2.2441 +    ...     value3 = integer(0, 10)
  2.2442 +    ...         [[[section3b-sub]]]
  2.2443 +    ...         value = string
  2.2444 +    ...     [section4]
  2.2445 +    ...     another_option = boolean()
  2.2446 +    ...     '''
  2.2447 +    >>> cs = my_cfg.split('\\n')
  2.2448 +    >>> ini = my_ini.split('\\n')
  2.2449 +    >>> cfg = ConfigObj(ini, configspec=cs)
  2.2450 +    >>> res = cfg.validate(vtor, preserve_errors=True)
  2.2451 +    >>> errors = []
  2.2452 +    >>> for entry in flatten_errors(cfg, res):
  2.2453 +    ...     section_list, key, error = entry
  2.2454 +    ...     section_list.insert(0, '[root]')
  2.2455 +    ...     if key is not None:
  2.2456 +    ...        section_list.append(key)
  2.2457 +    ...     else:
  2.2458 +    ...         section_list.append('[missing]')
  2.2459 +    ...     section_string = ', '.join(section_list)
  2.2460 +    ...     errors.append((section_string, ' = ', error))
  2.2461 +    >>> errors.sort()
  2.2462 +    >>> for entry in errors:
  2.2463 +    ...     print entry[0], entry[1], (entry[2] or 0)
  2.2464 +    [root], option2  =  0
  2.2465 +    [root], option3  =  the value "Bad_value" is of the wrong type.
  2.2466 +    [root], section1, option2  =  0
  2.2467 +    [root], section1, option3  =  the value "Bad_value" is of the wrong type.
  2.2468 +    [root], section2, another_option  =  the value "Probably" is of the wrong type.
  2.2469 +    [root], section3, section3b, section3b-sub, [missing]  =  0
  2.2470 +    [root], section3, section3b, value2  =  the value "a" is of the wrong type.
  2.2471 +    [root], section3, section3b, value3  =  the value "11" is too big.
  2.2472 +    [root], section4, [missing]  =  0
  2.2473 +    """
  2.2474 +    if levels is None:
  2.2475 +        # first time called
  2.2476 +        levels = []
  2.2477 +        results = []
  2.2478 +    if res is True:
  2.2479 +        return results
  2.2480 +    if res is False:
  2.2481 +        results.append((levels[:], None, False))
  2.2482 +        if levels:
  2.2483 +            levels.pop()
  2.2484 +        return results
  2.2485 +    for (key, val) in res.items():
  2.2486 +        if val == True:
  2.2487 +            continue
  2.2488 +        if isinstance(cfg.get(key), dict):
  2.2489 +            # Go down one level
  2.2490 +            levels.append(key)
  2.2491 +            flatten_errors(cfg[key], val, levels, results)
  2.2492 +            continue
  2.2493 +        results.append((levels[:], key, val))
  2.2494 +    #
  2.2495 +    # Go up one level
  2.2496 +    if levels:
  2.2497 +        levels.pop()
  2.2498 +    #
  2.2499 +    return results
  2.2500 +
  2.2501 +
  2.2502 +"""*A programming language is a medium of expression.* - Paul Graham"""