python/configobj/configobj.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/python/configobj/configobj.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2468 @@
     1.4 +# configobj.py
     1.5 +# A config file reader/writer that supports nested sections in config files.
     1.6 +# Copyright (C) 2005-2010 Michael Foord, Nicola Larosa
     1.7 +# E-mail: fuzzyman AT voidspace DOT org DOT uk
     1.8 +#         nico AT tekNico DOT net
     1.9 +
    1.10 +# ConfigObj 4
    1.11 +# http://www.voidspace.org.uk/python/configobj.html
    1.12 +
    1.13 +# Released subject to the BSD License
    1.14 +# Please see http://www.voidspace.org.uk/python/license.shtml
    1.15 +
    1.16 +# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
    1.17 +# For information about bugfixes, updates and support, please join the
    1.18 +# ConfigObj mailing list:
    1.19 +# http://lists.sourceforge.net/lists/listinfo/configobj-develop
    1.20 +# Comments, suggestions and bug reports welcome.
    1.21 +
    1.22 +from __future__ import generators
    1.23 +
    1.24 +import os
    1.25 +import re
    1.26 +import sys
    1.27 +
    1.28 +from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
    1.29 +
    1.30 +
    1.31 +# imported lazily to avoid startup performance hit if it isn't used
    1.32 +compiler = None
    1.33 +
    1.34 +# A dictionary mapping BOM to
    1.35 +# the encoding to decode with, and what to set the
    1.36 +# encoding attribute to.
    1.37 +BOMS = {
    1.38 +    BOM_UTF8: ('utf_8', None),
    1.39 +    BOM_UTF16_BE: ('utf16_be', 'utf_16'),
    1.40 +    BOM_UTF16_LE: ('utf16_le', 'utf_16'),
    1.41 +    BOM_UTF16: ('utf_16', 'utf_16'),
    1.42 +    }
    1.43 +# All legal variants of the BOM codecs.
    1.44 +# TODO: the list of aliases is not meant to be exhaustive, is there a
    1.45 +#   better way ?
    1.46 +BOM_LIST = {
    1.47 +    'utf_16': 'utf_16',
    1.48 +    'u16': 'utf_16',
    1.49 +    'utf16': 'utf_16',
    1.50 +    'utf-16': 'utf_16',
    1.51 +    'utf16_be': 'utf16_be',
    1.52 +    'utf_16_be': 'utf16_be',
    1.53 +    'utf-16be': 'utf16_be',
    1.54 +    'utf16_le': 'utf16_le',
    1.55 +    'utf_16_le': 'utf16_le',
    1.56 +    'utf-16le': 'utf16_le',
    1.57 +    'utf_8': 'utf_8',
    1.58 +    'u8': 'utf_8',
    1.59 +    'utf': 'utf_8',
    1.60 +    'utf8': 'utf_8',
    1.61 +    'utf-8': 'utf_8',
    1.62 +    }
    1.63 +
    1.64 +# Map of encodings to the BOM to write.
    1.65 +BOM_SET = {
    1.66 +    'utf_8': BOM_UTF8,
    1.67 +    'utf_16': BOM_UTF16,
    1.68 +    'utf16_be': BOM_UTF16_BE,
    1.69 +    'utf16_le': BOM_UTF16_LE,
    1.70 +    None: BOM_UTF8
    1.71 +    }
    1.72 +
    1.73 +
    1.74 +def match_utf8(encoding):
    1.75 +    return BOM_LIST.get(encoding.lower()) == 'utf_8'
    1.76 +
    1.77 +
    1.78 +# Quote strings used for writing values
    1.79 +squot = "'%s'"
    1.80 +dquot = '"%s"'
    1.81 +noquot = "%s"
    1.82 +wspace_plus = ' \r\n\v\t\'"'
    1.83 +tsquot = '"""%s"""'
    1.84 +tdquot = "'''%s'''"
    1.85 +
    1.86 +# Sentinel for use in getattr calls to replace hasattr
    1.87 +MISSING = object()
    1.88 +
    1.89 +__version__ = '4.7.2'
    1.90 +
    1.91 +try:
    1.92 +    any
    1.93 +except NameError:
    1.94 +    def any(iterable):
    1.95 +        for entry in iterable:
    1.96 +            if entry:
    1.97 +                return True
    1.98 +        return False
    1.99 +
   1.100 +
   1.101 +__all__ = (
   1.102 +    '__version__',
   1.103 +    'DEFAULT_INDENT_TYPE',
   1.104 +    'DEFAULT_INTERPOLATION',
   1.105 +    'ConfigObjError',
   1.106 +    'NestingError',
   1.107 +    'ParseError',
   1.108 +    'DuplicateError',
   1.109 +    'ConfigspecError',
   1.110 +    'ConfigObj',
   1.111 +    'SimpleVal',
   1.112 +    'InterpolationError',
   1.113 +    'InterpolationLoopError',
   1.114 +    'MissingInterpolationOption',
   1.115 +    'RepeatSectionError',
   1.116 +    'ReloadError',
   1.117 +    'UnreprError',
   1.118 +    'UnknownType',
   1.119 +    'flatten_errors',
   1.120 +    'get_extra_values'
   1.121 +)
   1.122 +
   1.123 +DEFAULT_INTERPOLATION = 'configparser'
   1.124 +DEFAULT_INDENT_TYPE = '    '
   1.125 +MAX_INTERPOL_DEPTH = 10
   1.126 +
   1.127 +OPTION_DEFAULTS = {
   1.128 +    'interpolation': True,
   1.129 +    'raise_errors': False,
   1.130 +    'list_values': True,
   1.131 +    'create_empty': False,
   1.132 +    'file_error': False,
   1.133 +    'configspec': None,
   1.134 +    'stringify': True,
   1.135 +    # option may be set to one of ('', ' ', '\t')
   1.136 +    'indent_type': None,
   1.137 +    'encoding': None,
   1.138 +    'default_encoding': None,
   1.139 +    'unrepr': False,
   1.140 +    'write_empty_values': False,
   1.141 +}
   1.142 +
   1.143 +
   1.144 +
   1.145 +def getObj(s):
   1.146 +    global compiler
   1.147 +    if compiler is None:
   1.148 +        import compiler
   1.149 +    s = "a=" + s
   1.150 +    p = compiler.parse(s)
   1.151 +    return p.getChildren()[1].getChildren()[0].getChildren()[1]
   1.152 +
   1.153 +
   1.154 +class UnknownType(Exception):
   1.155 +    pass
   1.156 +
   1.157 +
   1.158 +class Builder(object):
   1.159 +    
   1.160 +    def build(self, o):
   1.161 +        m = getattr(self, 'build_' + o.__class__.__name__, None)
   1.162 +        if m is None:
   1.163 +            raise UnknownType(o.__class__.__name__)
   1.164 +        return m(o)
   1.165 +    
   1.166 +    def build_List(self, o):
   1.167 +        return map(self.build, o.getChildren())
   1.168 +    
   1.169 +    def build_Const(self, o):
   1.170 +        return o.value
   1.171 +    
   1.172 +    def build_Dict(self, o):
   1.173 +        d = {}
   1.174 +        i = iter(map(self.build, o.getChildren()))
   1.175 +        for el in i:
   1.176 +            d[el] = i.next()
   1.177 +        return d
   1.178 +    
   1.179 +    def build_Tuple(self, o):
   1.180 +        return tuple(self.build_List(o))
   1.181 +    
   1.182 +    def build_Name(self, o):
   1.183 +        if o.name == 'None':
   1.184 +            return None
   1.185 +        if o.name == 'True':
   1.186 +            return True
   1.187 +        if o.name == 'False':
   1.188 +            return False
   1.189 +        
   1.190 +        # An undefined Name
   1.191 +        raise UnknownType('Undefined Name')
   1.192 +    
   1.193 +    def build_Add(self, o):
   1.194 +        real, imag = map(self.build_Const, o.getChildren())
   1.195 +        try:
   1.196 +            real = float(real)
   1.197 +        except TypeError:
   1.198 +            raise UnknownType('Add')
   1.199 +        if not isinstance(imag, complex) or imag.real != 0.0:
   1.200 +            raise UnknownType('Add')
   1.201 +        return real+imag
   1.202 +    
   1.203 +    def build_Getattr(self, o):
   1.204 +        parent = self.build(o.expr)
   1.205 +        return getattr(parent, o.attrname)
   1.206 +    
   1.207 +    def build_UnarySub(self, o):
   1.208 +        return -self.build_Const(o.getChildren()[0])
   1.209 +    
   1.210 +    def build_UnaryAdd(self, o):
   1.211 +        return self.build_Const(o.getChildren()[0])
   1.212 +
   1.213 +
   1.214 +_builder = Builder()
   1.215 +
   1.216 +
   1.217 +def unrepr(s):
   1.218 +    if not s:
   1.219 +        return s
   1.220 +    return _builder.build(getObj(s))
   1.221 +
   1.222 +
   1.223 +
   1.224 +class ConfigObjError(SyntaxError):
   1.225 +    """
   1.226 +    This is the base class for all errors that ConfigObj raises.
   1.227 +    It is a subclass of SyntaxError.
   1.228 +    """
   1.229 +    def __init__(self, message='', line_number=None, line=''):
   1.230 +        self.line = line
   1.231 +        self.line_number = line_number
   1.232 +        SyntaxError.__init__(self, message)
   1.233 +
   1.234 +
   1.235 +class NestingError(ConfigObjError):
   1.236 +    """
   1.237 +    This error indicates a level of nesting that doesn't match.
   1.238 +    """
   1.239 +
   1.240 +
   1.241 +class ParseError(ConfigObjError):
   1.242 +    """
   1.243 +    This error indicates that a line is badly written.
   1.244 +    It is neither a valid ``key = value`` line,
   1.245 +    nor a valid section marker line.
   1.246 +    """
   1.247 +
   1.248 +
   1.249 +class ReloadError(IOError):
   1.250 +    """
   1.251 +    A 'reload' operation failed.
   1.252 +    This exception is a subclass of ``IOError``.
   1.253 +    """
   1.254 +    def __init__(self):
   1.255 +        IOError.__init__(self, 'reload failed, filename is not set.')
   1.256 +
   1.257 +
   1.258 +class DuplicateError(ConfigObjError):
   1.259 +    """
   1.260 +    The keyword or section specified already exists.
   1.261 +    """
   1.262 +
   1.263 +
   1.264 +class ConfigspecError(ConfigObjError):
   1.265 +    """
   1.266 +    An error occured whilst parsing a configspec.
   1.267 +    """
   1.268 +
   1.269 +
   1.270 +class InterpolationError(ConfigObjError):
   1.271 +    """Base class for the two interpolation errors."""
   1.272 +
   1.273 +
   1.274 +class InterpolationLoopError(InterpolationError):
   1.275 +    """Maximum interpolation depth exceeded in string interpolation."""
   1.276 +
   1.277 +    def __init__(self, option):
   1.278 +        InterpolationError.__init__(
   1.279 +            self,
   1.280 +            'interpolation loop detected in value "%s".' % option)
   1.281 +
   1.282 +
   1.283 +class RepeatSectionError(ConfigObjError):
   1.284 +    """
   1.285 +    This error indicates additional sections in a section with a
   1.286 +    ``__many__`` (repeated) section.
   1.287 +    """
   1.288 +
   1.289 +
   1.290 +class MissingInterpolationOption(InterpolationError):
   1.291 +    """A value specified for interpolation was missing."""
   1.292 +    def __init__(self, option):
   1.293 +        msg = 'missing option "%s" in interpolation.' % option
   1.294 +        InterpolationError.__init__(self, msg)
   1.295 +
   1.296 +
   1.297 +class UnreprError(ConfigObjError):
   1.298 +    """An error parsing in unrepr mode."""
   1.299 +
   1.300 +
   1.301 +
   1.302 +class InterpolationEngine(object):
   1.303 +    """
   1.304 +    A helper class to help perform string interpolation.
   1.305 +
   1.306 +    This class is an abstract base class; its descendants perform
   1.307 +    the actual work.
   1.308 +    """
   1.309 +
   1.310 +    # compiled regexp to use in self.interpolate()
   1.311 +    _KEYCRE = re.compile(r"%\(([^)]*)\)s")
   1.312 +    _cookie = '%'
   1.313 +
   1.314 +    def __init__(self, section):
   1.315 +        # the Section instance that "owns" this engine
   1.316 +        self.section = section
   1.317 +
   1.318 +
   1.319 +    def interpolate(self, key, value):
   1.320 +        # short-cut
   1.321 +        if not self._cookie in value:
   1.322 +            return value
   1.323 +        
   1.324 +        def recursive_interpolate(key, value, section, backtrail):
   1.325 +            """The function that does the actual work.
   1.326 +
   1.327 +            ``value``: the string we're trying to interpolate.
   1.328 +            ``section``: the section in which that string was found
   1.329 +            ``backtrail``: a dict to keep track of where we've been,
   1.330 +            to detect and prevent infinite recursion loops
   1.331 +
   1.332 +            This is similar to a depth-first-search algorithm.
   1.333 +            """
   1.334 +            # Have we been here already?
   1.335 +            if (key, section.name) in backtrail:
   1.336 +                # Yes - infinite loop detected
   1.337 +                raise InterpolationLoopError(key)
   1.338 +            # Place a marker on our backtrail so we won't come back here again
   1.339 +            backtrail[(key, section.name)] = 1
   1.340 +
   1.341 +            # Now start the actual work
   1.342 +            match = self._KEYCRE.search(value)
   1.343 +            while match:
   1.344 +                # The actual parsing of the match is implementation-dependent,
   1.345 +                # so delegate to our helper function
   1.346 +                k, v, s = self._parse_match(match)
   1.347 +                if k is None:
   1.348 +                    # That's the signal that no further interpolation is needed
   1.349 +                    replacement = v
   1.350 +                else:
   1.351 +                    # Further interpolation may be needed to obtain final value
   1.352 +                    replacement = recursive_interpolate(k, v, s, backtrail)
   1.353 +                # Replace the matched string with its final value
   1.354 +                start, end = match.span()
   1.355 +                value = ''.join((value[:start], replacement, value[end:]))
   1.356 +                new_search_start = start + len(replacement)
   1.357 +                # Pick up the next interpolation key, if any, for next time
   1.358 +                # through the while loop
   1.359 +                match = self._KEYCRE.search(value, new_search_start)
   1.360 +
   1.361 +            # Now safe to come back here again; remove marker from backtrail
   1.362 +            del backtrail[(key, section.name)]
   1.363 +
   1.364 +            return value
   1.365 +
   1.366 +        # Back in interpolate(), all we have to do is kick off the recursive
   1.367 +        # function with appropriate starting values
   1.368 +        value = recursive_interpolate(key, value, self.section, {})
   1.369 +        return value
   1.370 +
   1.371 +
   1.372 +    def _fetch(self, key):
   1.373 +        """Helper function to fetch values from owning section.
   1.374 +
   1.375 +        Returns a 2-tuple: the value, and the section where it was found.
   1.376 +        """
   1.377 +        # switch off interpolation before we try and fetch anything !
   1.378 +        save_interp = self.section.main.interpolation
   1.379 +        self.section.main.interpolation = False
   1.380 +
   1.381 +        # Start at section that "owns" this InterpolationEngine
   1.382 +        current_section = self.section
   1.383 +        while True:
   1.384 +            # try the current section first
   1.385 +            val = current_section.get(key)
   1.386 +            if val is not None and not isinstance(val, Section):
   1.387 +                break
   1.388 +            # try "DEFAULT" next
   1.389 +            val = current_section.get('DEFAULT', {}).get(key)
   1.390 +            if val is not None and not isinstance(val, Section):
   1.391 +                break
   1.392 +            # move up to parent and try again
   1.393 +            # top-level's parent is itself
   1.394 +            if current_section.parent is current_section:
   1.395 +                # reached top level, time to give up
   1.396 +                break
   1.397 +            current_section = current_section.parent
   1.398 +
   1.399 +        # restore interpolation to previous value before returning
   1.400 +        self.section.main.interpolation = save_interp
   1.401 +        if val is None:
   1.402 +            raise MissingInterpolationOption(key)
   1.403 +        return val, current_section
   1.404 +
   1.405 +
   1.406 +    def _parse_match(self, match):
   1.407 +        """Implementation-dependent helper function.
   1.408 +
   1.409 +        Will be passed a match object corresponding to the interpolation
   1.410 +        key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
   1.411 +        key in the appropriate config file section (using the ``_fetch()``
   1.412 +        helper function) and return a 3-tuple: (key, value, section)
   1.413 +
   1.414 +        ``key`` is the name of the key we're looking for
   1.415 +        ``value`` is the value found for that key
   1.416 +        ``section`` is a reference to the section where it was found
   1.417 +
   1.418 +        ``key`` and ``section`` should be None if no further
   1.419 +        interpolation should be performed on the resulting value
   1.420 +        (e.g., if we interpolated "$$" and returned "$").
   1.421 +        """
   1.422 +        raise NotImplementedError()
   1.423 +    
   1.424 +
   1.425 +
   1.426 +class ConfigParserInterpolation(InterpolationEngine):
   1.427 +    """Behaves like ConfigParser."""
   1.428 +    _cookie = '%'
   1.429 +    _KEYCRE = re.compile(r"%\(([^)]*)\)s")
   1.430 +
   1.431 +    def _parse_match(self, match):
   1.432 +        key = match.group(1)
   1.433 +        value, section = self._fetch(key)
   1.434 +        return key, value, section
   1.435 +
   1.436 +
   1.437 +
   1.438 +class TemplateInterpolation(InterpolationEngine):
   1.439 +    """Behaves like string.Template."""
   1.440 +    _cookie = '$'
   1.441 +    _delimiter = '$'
   1.442 +    _KEYCRE = re.compile(r"""
   1.443 +        \$(?:
   1.444 +          (?P<escaped>\$)              |   # Two $ signs
   1.445 +          (?P<named>[_a-z][_a-z0-9]*)  |   # $name format
   1.446 +          {(?P<braced>[^}]*)}              # ${name} format
   1.447 +        )
   1.448 +        """, re.IGNORECASE | re.VERBOSE)
   1.449 +
   1.450 +    def _parse_match(self, match):
   1.451 +        # Valid name (in or out of braces): fetch value from section
   1.452 +        key = match.group('named') or match.group('braced')
   1.453 +        if key is not None:
   1.454 +            value, section = self._fetch(key)
   1.455 +            return key, value, section
   1.456 +        # Escaped delimiter (e.g., $$): return single delimiter
   1.457 +        if match.group('escaped') is not None:
   1.458 +            # Return None for key and section to indicate it's time to stop
   1.459 +            return None, self._delimiter, None
   1.460 +        # Anything else: ignore completely, just return it unchanged
   1.461 +        return None, match.group(), None
   1.462 +
   1.463 +
   1.464 +interpolation_engines = {
   1.465 +    'configparser': ConfigParserInterpolation,
   1.466 +    'template': TemplateInterpolation,
   1.467 +}
   1.468 +
   1.469 +
   1.470 +def __newobj__(cls, *args):
   1.471 +    # Hack for pickle
   1.472 +    return cls.__new__(cls, *args) 
   1.473 +
   1.474 +class Section(dict):
   1.475 +    """
   1.476 +    A dictionary-like object that represents a section in a config file.
   1.477 +    
   1.478 +    It does string interpolation if the 'interpolation' attribute
   1.479 +    of the 'main' object is set to True.
   1.480 +    
   1.481 +    Interpolation is tried first from this object, then from the 'DEFAULT'
   1.482 +    section of this object, next from the parent and its 'DEFAULT' section,
   1.483 +    and so on until the main object is reached.
   1.484 +    
   1.485 +    A Section will behave like an ordered dictionary - following the
   1.486 +    order of the ``scalars`` and ``sections`` attributes.
   1.487 +    You can use this to change the order of members.
   1.488 +    
   1.489 +    Iteration follows the order: scalars, then sections.
   1.490 +    """
   1.491 +
   1.492 +    
   1.493 +    def __setstate__(self, state):
   1.494 +        dict.update(self, state[0])
   1.495 +        self.__dict__.update(state[1])
   1.496 +
   1.497 +    def __reduce__(self):
   1.498 +        state = (dict(self), self.__dict__)
   1.499 +        return (__newobj__, (self.__class__,), state)
   1.500 +    
   1.501 +    
   1.502 +    def __init__(self, parent, depth, main, indict=None, name=None):
   1.503 +        """
   1.504 +        * parent is the section above
   1.505 +        * depth is the depth level of this section
   1.506 +        * main is the main ConfigObj
   1.507 +        * indict is a dictionary to initialise the section with
   1.508 +        """
   1.509 +        if indict is None:
   1.510 +            indict = {}
   1.511 +        dict.__init__(self)
   1.512 +        # used for nesting level *and* interpolation
   1.513 +        self.parent = parent
   1.514 +        # used for the interpolation attribute
   1.515 +        self.main = main
   1.516 +        # level of nesting depth of this Section
   1.517 +        self.depth = depth
   1.518 +        # purely for information
   1.519 +        self.name = name
   1.520 +        #
   1.521 +        self._initialise()
   1.522 +        # we do this explicitly so that __setitem__ is used properly
   1.523 +        # (rather than just passing to ``dict.__init__``)
   1.524 +        for entry, value in indict.iteritems():
   1.525 +            self[entry] = value
   1.526 +            
   1.527 +            
   1.528 +    def _initialise(self):
   1.529 +        # the sequence of scalar values in this Section
   1.530 +        self.scalars = []
   1.531 +        # the sequence of sections in this Section
   1.532 +        self.sections = []
   1.533 +        # for comments :-)
   1.534 +        self.comments = {}
   1.535 +        self.inline_comments = {}
   1.536 +        # the configspec
   1.537 +        self.configspec = None
   1.538 +        # for defaults
   1.539 +        self.defaults = []
   1.540 +        self.default_values = {}
   1.541 +        self.extra_values = []
   1.542 +        self._created = False
   1.543 +
   1.544 +
   1.545 +    def _interpolate(self, key, value):
   1.546 +        try:
   1.547 +            # do we already have an interpolation engine?
   1.548 +            engine = self._interpolation_engine
   1.549 +        except AttributeError:
   1.550 +            # not yet: first time running _interpolate(), so pick the engine
   1.551 +            name = self.main.interpolation
   1.552 +            if name == True:  # note that "if name:" would be incorrect here
   1.553 +                # backwards-compatibility: interpolation=True means use default
   1.554 +                name = DEFAULT_INTERPOLATION
   1.555 +            name = name.lower()  # so that "Template", "template", etc. all work
   1.556 +            class_ = interpolation_engines.get(name, None)
   1.557 +            if class_ is None:
   1.558 +                # invalid value for self.main.interpolation
   1.559 +                self.main.interpolation = False
   1.560 +                return value
   1.561 +            else:
   1.562 +                # save reference to engine so we don't have to do this again
   1.563 +                engine = self._interpolation_engine = class_(self)
   1.564 +        # let the engine do the actual work
   1.565 +        return engine.interpolate(key, value)
   1.566 +
   1.567 +
   1.568 +    def __getitem__(self, key):
   1.569 +        """Fetch the item and do string interpolation."""
   1.570 +        val = dict.__getitem__(self, key)
   1.571 +        if self.main.interpolation: 
   1.572 +            if isinstance(val, basestring):
   1.573 +                return self._interpolate(key, val)
   1.574 +            if isinstance(val, list):
   1.575 +                def _check(entry):
   1.576 +                    if isinstance(entry, basestring):
   1.577 +                        return self._interpolate(key, entry)
   1.578 +                    return entry
   1.579 +                new = [_check(entry) for entry in val]
   1.580 +                if new != val:
   1.581 +                    return new
   1.582 +        return val
   1.583 +
   1.584 +
   1.585 +    def __setitem__(self, key, value, unrepr=False):
   1.586 +        """
   1.587 +        Correctly set a value.
   1.588 +        
   1.589 +        Making dictionary values Section instances.
   1.590 +        (We have to special case 'Section' instances - which are also dicts)
   1.591 +        
   1.592 +        Keys must be strings.
   1.593 +        Values need only be strings (or lists of strings) if
   1.594 +        ``main.stringify`` is set.
   1.595 +        
   1.596 +        ``unrepr`` must be set when setting a value to a dictionary, without
   1.597 +        creating a new sub-section.
   1.598 +        """
   1.599 +        if not isinstance(key, basestring):
   1.600 +            raise ValueError('The key "%s" is not a string.' % key)
   1.601 +        
   1.602 +        # add the comment
   1.603 +        if key not in self.comments:
   1.604 +            self.comments[key] = []
   1.605 +            self.inline_comments[key] = ''
   1.606 +        # remove the entry from defaults
   1.607 +        if key in self.defaults:
   1.608 +            self.defaults.remove(key)
   1.609 +        #
   1.610 +        if isinstance(value, Section):
   1.611 +            if key not in self:
   1.612 +                self.sections.append(key)
   1.613 +            dict.__setitem__(self, key, value)
   1.614 +        elif isinstance(value, dict) and not unrepr:
   1.615 +            # First create the new depth level,
   1.616 +            # then create the section
   1.617 +            if key not in self:
   1.618 +                self.sections.append(key)
   1.619 +            new_depth = self.depth + 1
   1.620 +            dict.__setitem__(
   1.621 +                self,
   1.622 +                key,
   1.623 +                Section(
   1.624 +                    self,
   1.625 +                    new_depth,
   1.626 +                    self.main,
   1.627 +                    indict=value,
   1.628 +                    name=key))
   1.629 +        else:
   1.630 +            if key not in self:
   1.631 +                self.scalars.append(key)
   1.632 +            if not self.main.stringify:
   1.633 +                if isinstance(value, basestring):
   1.634 +                    pass
   1.635 +                elif isinstance(value, (list, tuple)):
   1.636 +                    for entry in value:
   1.637 +                        if not isinstance(entry, basestring):
   1.638 +                            raise TypeError('Value is not a string "%s".' % entry)
   1.639 +                else:
   1.640 +                    raise TypeError('Value is not a string "%s".' % value)
   1.641 +            dict.__setitem__(self, key, value)
   1.642 +
   1.643 +
   1.644 +    def __delitem__(self, key):
   1.645 +        """Remove items from the sequence when deleting."""
   1.646 +        dict. __delitem__(self, key)
   1.647 +        if key in self.scalars:
   1.648 +            self.scalars.remove(key)
   1.649 +        else:
   1.650 +            self.sections.remove(key)
   1.651 +        del self.comments[key]
   1.652 +        del self.inline_comments[key]
   1.653 +
   1.654 +
   1.655 +    def get(self, key, default=None):
   1.656 +        """A version of ``get`` that doesn't bypass string interpolation."""
   1.657 +        try:
   1.658 +            return self[key]
   1.659 +        except KeyError:
   1.660 +            return default
   1.661 +
   1.662 +
   1.663 +    def update(self, indict):
   1.664 +        """
   1.665 +        A version of update that uses our ``__setitem__``.
   1.666 +        """
   1.667 +        for entry in indict:
   1.668 +            self[entry] = indict[entry]
   1.669 +
   1.670 +
   1.671 +    def pop(self, key, default=MISSING):
   1.672 +        """
   1.673 +        'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
   1.674 +        If key is not found, d is returned if given, otherwise KeyError is raised'
   1.675 +        """
   1.676 +        try:
   1.677 +            val = self[key]
   1.678 +        except KeyError:
   1.679 +            if default is MISSING:
   1.680 +                raise
   1.681 +            val = default
   1.682 +        else:
   1.683 +            del self[key]
   1.684 +        return val
   1.685 +
   1.686 +
   1.687 +    def popitem(self):
   1.688 +        """Pops the first (key,val)"""
   1.689 +        sequence = (self.scalars + self.sections)
   1.690 +        if not sequence:
   1.691 +            raise KeyError(": 'popitem(): dictionary is empty'")
   1.692 +        key = sequence[0]
   1.693 +        val =  self[key]
   1.694 +        del self[key]
   1.695 +        return key, val
   1.696 +
   1.697 +
   1.698 +    def clear(self):
   1.699 +        """
   1.700 +        A version of clear that also affects scalars/sections
   1.701 +        Also clears comments and configspec.
   1.702 +        
   1.703 +        Leaves other attributes alone :
   1.704 +            depth/main/parent are not affected
   1.705 +        """
   1.706 +        dict.clear(self)
   1.707 +        self.scalars = []
   1.708 +        self.sections = []
   1.709 +        self.comments = {}
   1.710 +        self.inline_comments = {}
   1.711 +        self.configspec = None
   1.712 +        self.defaults = []
   1.713 +        self.extra_values = []
   1.714 +
   1.715 +
   1.716 +    def setdefault(self, key, default=None):
   1.717 +        """A version of setdefault that sets sequence if appropriate."""
   1.718 +        try:
   1.719 +            return self[key]
   1.720 +        except KeyError:
   1.721 +            self[key] = default
   1.722 +            return self[key]
   1.723 +
   1.724 +
   1.725 +    def items(self):
   1.726 +        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
   1.727 +        return zip((self.scalars + self.sections), self.values())
   1.728 +
   1.729 +
   1.730 +    def keys(self):
   1.731 +        """D.keys() -> list of D's keys"""
   1.732 +        return (self.scalars + self.sections)
   1.733 +
   1.734 +
   1.735 +    def values(self):
   1.736 +        """D.values() -> list of D's values"""
   1.737 +        return [self[key] for key in (self.scalars + self.sections)]
   1.738 +
   1.739 +
   1.740 +    def iteritems(self):
   1.741 +        """D.iteritems() -> an iterator over the (key, value) items of D"""
   1.742 +        return iter(self.items())
   1.743 +
   1.744 +
   1.745 +    def iterkeys(self):
   1.746 +        """D.iterkeys() -> an iterator over the keys of D"""
   1.747 +        return iter((self.scalars + self.sections))
   1.748 +
   1.749 +    __iter__ = iterkeys
   1.750 +
   1.751 +
   1.752 +    def itervalues(self):
   1.753 +        """D.itervalues() -> an iterator over the values of D"""
   1.754 +        return iter(self.values())
   1.755 +
   1.756 +
   1.757 +    def __repr__(self):
   1.758 +        """x.__repr__() <==> repr(x)"""
   1.759 +        def _getval(key):
   1.760 +            try:
   1.761 +                return self[key]
   1.762 +            except MissingInterpolationOption:
   1.763 +                return dict.__getitem__(self, key)
   1.764 +        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(_getval(key))))
   1.765 +            for key in (self.scalars + self.sections)])
   1.766 +
   1.767 +    __str__ = __repr__
   1.768 +    __str__.__doc__ = "x.__str__() <==> str(x)"
   1.769 +
   1.770 +
   1.771 +    # Extra methods - not in a normal dictionary
   1.772 +
   1.773 +    def dict(self):
   1.774 +        """
   1.775 +        Return a deepcopy of self as a dictionary.
   1.776 +        
   1.777 +        All members that are ``Section`` instances are recursively turned to
   1.778 +        ordinary dictionaries - by calling their ``dict`` method.
   1.779 +        
   1.780 +        >>> n = a.dict()
   1.781 +        >>> n == a
   1.782 +        1
   1.783 +        >>> n is a
   1.784 +        0
   1.785 +        """
   1.786 +        newdict = {}
   1.787 +        for entry in self:
   1.788 +            this_entry = self[entry]
   1.789 +            if isinstance(this_entry, Section):
   1.790 +                this_entry = this_entry.dict()
   1.791 +            elif isinstance(this_entry, list):
   1.792 +                # create a copy rather than a reference
   1.793 +                this_entry = list(this_entry)
   1.794 +            elif isinstance(this_entry, tuple):
   1.795 +                # create a copy rather than a reference
   1.796 +                this_entry = tuple(this_entry)
   1.797 +            newdict[entry] = this_entry
   1.798 +        return newdict
   1.799 +
   1.800 +
   1.801 +    def merge(self, indict):
   1.802 +        """
   1.803 +        A recursive update - useful for merging config files.
   1.804 +        
   1.805 +        >>> a = '''[section1]
   1.806 +        ...     option1 = True
   1.807 +        ...     [[subsection]]
   1.808 +        ...     more_options = False
   1.809 +        ...     # end of file'''.splitlines()
   1.810 +        >>> b = '''# File is user.ini
   1.811 +        ...     [section1]
   1.812 +        ...     option1 = False
   1.813 +        ...     # end of file'''.splitlines()
   1.814 +        >>> c1 = ConfigObj(b)
   1.815 +        >>> c2 = ConfigObj(a)
   1.816 +        >>> c2.merge(c1)
   1.817 +        >>> c2
   1.818 +        ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
   1.819 +        """
   1.820 +        for key, val in indict.items():
   1.821 +            if (key in self and isinstance(self[key], dict) and
   1.822 +                                isinstance(val, dict)):
   1.823 +                self[key].merge(val)
   1.824 +            else:   
   1.825 +                self[key] = val
   1.826 +
   1.827 +
   1.828 +    def rename(self, oldkey, newkey):
   1.829 +        """
   1.830 +        Change a keyname to another, without changing position in sequence.
   1.831 +        
   1.832 +        Implemented so that transformations can be made on keys,
   1.833 +        as well as on values. (used by encode and decode)
   1.834 +        
   1.835 +        Also renames comments.
   1.836 +        """
   1.837 +        if oldkey in self.scalars:
   1.838 +            the_list = self.scalars
   1.839 +        elif oldkey in self.sections:
   1.840 +            the_list = self.sections
   1.841 +        else:
   1.842 +            raise KeyError('Key "%s" not found.' % oldkey)
   1.843 +        pos = the_list.index(oldkey)
   1.844 +        #
   1.845 +        val = self[oldkey]
   1.846 +        dict.__delitem__(self, oldkey)
   1.847 +        dict.__setitem__(self, newkey, val)
   1.848 +        the_list.remove(oldkey)
   1.849 +        the_list.insert(pos, newkey)
   1.850 +        comm = self.comments[oldkey]
   1.851 +        inline_comment = self.inline_comments[oldkey]
   1.852 +        del self.comments[oldkey]
   1.853 +        del self.inline_comments[oldkey]
   1.854 +        self.comments[newkey] = comm
   1.855 +        self.inline_comments[newkey] = inline_comment
   1.856 +
   1.857 +
   1.858 +    def walk(self, function, raise_errors=True,
   1.859 +            call_on_sections=False, **keywargs):
   1.860 +        """
   1.861 +        Walk every member and call a function on the keyword and value.
   1.862 +        
   1.863 +        Return a dictionary of the return values
   1.864 +        
   1.865 +        If the function raises an exception, raise the errror
   1.866 +        unless ``raise_errors=False``, in which case set the return value to
   1.867 +        ``False``.
   1.868 +        
   1.869 +        Any unrecognised keyword arguments you pass to walk, will be pased on
   1.870 +        to the function you pass in.
   1.871 +        
   1.872 +        Note: if ``call_on_sections`` is ``True`` then - on encountering a
   1.873 +        subsection, *first* the function is called for the *whole* subsection,
   1.874 +        and then recurses into it's members. This means your function must be
   1.875 +        able to handle strings, dictionaries and lists. This allows you
   1.876 +        to change the key of subsections as well as for ordinary members. The
   1.877 +        return value when called on the whole subsection has to be discarded.
   1.878 +        
   1.879 +        See  the encode and decode methods for examples, including functions.
   1.880 +        
   1.881 +        .. admonition:: caution
   1.882 +        
   1.883 +            You can use ``walk`` to transform the names of members of a section
   1.884 +            but you mustn't add or delete members.
   1.885 +        
   1.886 +        >>> config = '''[XXXXsection]
   1.887 +        ... XXXXkey = XXXXvalue'''.splitlines()
   1.888 +        >>> cfg = ConfigObj(config)
   1.889 +        >>> cfg
   1.890 +        ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
   1.891 +        >>> def transform(section, key):
   1.892 +        ...     val = section[key]
   1.893 +        ...     newkey = key.replace('XXXX', 'CLIENT1')
   1.894 +        ...     section.rename(key, newkey)
   1.895 +        ...     if isinstance(val, (tuple, list, dict)):
   1.896 +        ...         pass
   1.897 +        ...     else:
   1.898 +        ...         val = val.replace('XXXX', 'CLIENT1')
   1.899 +        ...         section[newkey] = val
   1.900 +        >>> cfg.walk(transform, call_on_sections=True)
   1.901 +        {'CLIENT1section': {'CLIENT1key': None}}
   1.902 +        >>> cfg
   1.903 +        ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
   1.904 +        """
   1.905 +        out = {}
   1.906 +        # scalars first
   1.907 +        for i in range(len(self.scalars)):
   1.908 +            entry = self.scalars[i]
   1.909 +            try:
   1.910 +                val = function(self, entry, **keywargs)
   1.911 +                # bound again in case name has changed
   1.912 +                entry = self.scalars[i]
   1.913 +                out[entry] = val
   1.914 +            except Exception:
   1.915 +                if raise_errors:
   1.916 +                    raise
   1.917 +                else:
   1.918 +                    entry = self.scalars[i]
   1.919 +                    out[entry] = False
   1.920 +        # then sections
   1.921 +        for i in range(len(self.sections)):
   1.922 +            entry = self.sections[i]
   1.923 +            if call_on_sections:
   1.924 +                try:
   1.925 +                    function(self, entry, **keywargs)
   1.926 +                except Exception:
   1.927 +                    if raise_errors:
   1.928 +                        raise
   1.929 +                    else:
   1.930 +                        entry = self.sections[i]
   1.931 +                        out[entry] = False
   1.932 +                # bound again in case name has changed
   1.933 +                entry = self.sections[i]
   1.934 +            # previous result is discarded
   1.935 +            out[entry] = self[entry].walk(
   1.936 +                function,
   1.937 +                raise_errors=raise_errors,
   1.938 +                call_on_sections=call_on_sections,
   1.939 +                **keywargs)
   1.940 +        return out
   1.941 +
   1.942 +
   1.943 +    def as_bool(self, key):
   1.944 +        """
   1.945 +        Accepts a key as input. The corresponding value must be a string or
   1.946 +        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
   1.947 +        retain compatibility with Python 2.2.
   1.948 +        
   1.949 +        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
   1.950 +        ``True``.
   1.951 +        
   1.952 +        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
   1.953 +        ``False``.
   1.954 +        
   1.955 +        ``as_bool`` is not case sensitive.
   1.956 +        
   1.957 +        Any other input will raise a ``ValueError``.
   1.958 +        
   1.959 +        >>> a = ConfigObj()
   1.960 +        >>> a['a'] = 'fish'
   1.961 +        >>> a.as_bool('a')
   1.962 +        Traceback (most recent call last):
   1.963 +        ValueError: Value "fish" is neither True nor False
   1.964 +        >>> a['b'] = 'True'
   1.965 +        >>> a.as_bool('b')
   1.966 +        1
   1.967 +        >>> a['b'] = 'off'
   1.968 +        >>> a.as_bool('b')
   1.969 +        0
   1.970 +        """
   1.971 +        val = self[key]
   1.972 +        if val == True:
   1.973 +            return True
   1.974 +        elif val == False:
   1.975 +            return False
   1.976 +        else:
   1.977 +            try:
   1.978 +                if not isinstance(val, basestring):
   1.979 +                    # TODO: Why do we raise a KeyError here?
   1.980 +                    raise KeyError()
   1.981 +                else:
   1.982 +                    return self.main._bools[val.lower()]
   1.983 +            except KeyError:
   1.984 +                raise ValueError('Value "%s" is neither True nor False' % val)
   1.985 +
   1.986 +
   1.987 +    def as_int(self, key):
   1.988 +        """
   1.989 +        A convenience method which coerces the specified value to an integer.
   1.990 +        
   1.991 +        If the value is an invalid literal for ``int``, a ``ValueError`` will
   1.992 +        be raised.
   1.993 +        
   1.994 +        >>> a = ConfigObj()
   1.995 +        >>> a['a'] = 'fish'
   1.996 +        >>> a.as_int('a')
   1.997 +        Traceback (most recent call last):
   1.998 +        ValueError: invalid literal for int() with base 10: 'fish'
   1.999 +        >>> a['b'] = '1'
  1.1000 +        >>> a.as_int('b')
  1.1001 +        1
  1.1002 +        >>> a['b'] = '3.2'
  1.1003 +        >>> a.as_int('b')
  1.1004 +        Traceback (most recent call last):
  1.1005 +        ValueError: invalid literal for int() with base 10: '3.2'
  1.1006 +        """
  1.1007 +        return int(self[key])
  1.1008 +
  1.1009 +
  1.1010 +    def as_float(self, key):
  1.1011 +        """
  1.1012 +        A convenience method which coerces the specified value to a float.
  1.1013 +        
  1.1014 +        If the value is an invalid literal for ``float``, a ``ValueError`` will
  1.1015 +        be raised.
  1.1016 +        
  1.1017 +        >>> a = ConfigObj()
  1.1018 +        >>> a['a'] = 'fish'
  1.1019 +        >>> a.as_float('a')
  1.1020 +        Traceback (most recent call last):
  1.1021 +        ValueError: invalid literal for float(): fish
  1.1022 +        >>> a['b'] = '1'
  1.1023 +        >>> a.as_float('b')
  1.1024 +        1.0
  1.1025 +        >>> a['b'] = '3.2'
  1.1026 +        >>> a.as_float('b')
  1.1027 +        3.2000000000000002
  1.1028 +        """
  1.1029 +        return float(self[key])
  1.1030 +    
  1.1031 +    
  1.1032 +    def as_list(self, key):
  1.1033 +        """
  1.1034 +        A convenience method which fetches the specified value, guaranteeing
  1.1035 +        that it is a list.
  1.1036 +        
  1.1037 +        >>> a = ConfigObj()
  1.1038 +        >>> a['a'] = 1
  1.1039 +        >>> a.as_list('a')
  1.1040 +        [1]
  1.1041 +        >>> a['a'] = (1,)
  1.1042 +        >>> a.as_list('a')
  1.1043 +        [1]
  1.1044 +        >>> a['a'] = [1]
  1.1045 +        >>> a.as_list('a')
  1.1046 +        [1]
  1.1047 +        """
  1.1048 +        result = self[key]
  1.1049 +        if isinstance(result, (tuple, list)):
  1.1050 +            return list(result)
  1.1051 +        return [result]
  1.1052 +        
  1.1053 +
  1.1054 +    def restore_default(self, key):
  1.1055 +        """
  1.1056 +        Restore (and return) default value for the specified key.
  1.1057 +        
  1.1058 +        This method will only work for a ConfigObj that was created
  1.1059 +        with a configspec and has been validated.
  1.1060 +        
  1.1061 +        If there is no default value for this key, ``KeyError`` is raised.
  1.1062 +        """
  1.1063 +        default = self.default_values[key]
  1.1064 +        dict.__setitem__(self, key, default)
  1.1065 +        if key not in self.defaults:
  1.1066 +            self.defaults.append(key)
  1.1067 +        return default
  1.1068 +
  1.1069 +    
  1.1070 +    def restore_defaults(self):
  1.1071 +        """
  1.1072 +        Recursively restore default values to all members
  1.1073 +        that have them.
  1.1074 +        
  1.1075 +        This method will only work for a ConfigObj that was created
  1.1076 +        with a configspec and has been validated.
  1.1077 +        
  1.1078 +        It doesn't delete or modify entries without default values.
  1.1079 +        """
  1.1080 +        for key in self.default_values:
  1.1081 +            self.restore_default(key)
  1.1082 +            
  1.1083 +        for section in self.sections:
  1.1084 +            self[section].restore_defaults()
  1.1085 +
  1.1086 +
  1.1087 +class ConfigObj(Section):
  1.1088 +    """An object to read, create, and write config files."""
  1.1089 +
  1.1090 +    _keyword = re.compile(r'''^ # line start
  1.1091 +        (\s*)                   # indentation
  1.1092 +        (                       # keyword
  1.1093 +            (?:".*?")|          # double quotes
  1.1094 +            (?:'.*?')|          # single quotes
  1.1095 +            (?:[^'"=].*?)       # no quotes
  1.1096 +        )
  1.1097 +        \s*=\s*                 # divider
  1.1098 +        (.*)                    # value (including list values and comments)
  1.1099 +        $   # line end
  1.1100 +        ''',
  1.1101 +        re.VERBOSE)
  1.1102 +
  1.1103 +    _sectionmarker = re.compile(r'''^
  1.1104 +        (\s*)                     # 1: indentation
  1.1105 +        ((?:\[\s*)+)              # 2: section marker open
  1.1106 +        (                         # 3: section name open
  1.1107 +            (?:"\s*\S.*?\s*")|    # at least one non-space with double quotes
  1.1108 +            (?:'\s*\S.*?\s*')|    # at least one non-space with single quotes
  1.1109 +            (?:[^'"\s].*?)        # at least one non-space unquoted
  1.1110 +        )                         # section name close
  1.1111 +        ((?:\s*\])+)              # 4: section marker close
  1.1112 +        \s*(\#.*)?                # 5: optional comment
  1.1113 +        $''',
  1.1114 +        re.VERBOSE)
  1.1115 +
  1.1116 +    # this regexp pulls list values out as a single string
  1.1117 +    # or single values and comments
  1.1118 +    # FIXME: this regex adds a '' to the end of comma terminated lists
  1.1119 +    #   workaround in ``_handle_value``
  1.1120 +    _valueexp = re.compile(r'''^
  1.1121 +        (?:
  1.1122 +            (?:
  1.1123 +                (
  1.1124 +                    (?:
  1.1125 +                        (?:
  1.1126 +                            (?:".*?")|              # double quotes
  1.1127 +                            (?:'.*?')|              # single quotes
  1.1128 +                            (?:[^'",\#][^,\#]*?)    # unquoted
  1.1129 +                        )
  1.1130 +                        \s*,\s*                     # comma
  1.1131 +                    )*      # match all list items ending in a comma (if any)
  1.1132 +                )
  1.1133 +                (
  1.1134 +                    (?:".*?")|                      # double quotes
  1.1135 +                    (?:'.*?')|                      # single quotes
  1.1136 +                    (?:[^'",\#\s][^,]*?)|           # unquoted
  1.1137 +                    (?:(?<!,))                      # Empty value
  1.1138 +                )?          # last item in a list - or string value
  1.1139 +            )|
  1.1140 +            (,)             # alternatively a single comma - empty list
  1.1141 +        )
  1.1142 +        \s*(\#.*)?          # optional comment
  1.1143 +        $''',
  1.1144 +        re.VERBOSE)
  1.1145 +
  1.1146 +    # use findall to get the members of a list value
  1.1147 +    _listvalueexp = re.compile(r'''
  1.1148 +        (
  1.1149 +            (?:".*?")|          # double quotes
  1.1150 +            (?:'.*?')|          # single quotes
  1.1151 +            (?:[^'",\#]?.*?)       # unquoted
  1.1152 +        )
  1.1153 +        \s*,\s*                 # comma
  1.1154 +        ''',
  1.1155 +        re.VERBOSE)
  1.1156 +
  1.1157 +    # this regexp is used for the value
  1.1158 +    # when lists are switched off
  1.1159 +    _nolistvalue = re.compile(r'''^
  1.1160 +        (
  1.1161 +            (?:".*?")|          # double quotes
  1.1162 +            (?:'.*?')|          # single quotes
  1.1163 +            (?:[^'"\#].*?)|     # unquoted
  1.1164 +            (?:)                # Empty value
  1.1165 +        )
  1.1166 +        \s*(\#.*)?              # optional comment
  1.1167 +        $''',
  1.1168 +        re.VERBOSE)
  1.1169 +
  1.1170 +    # regexes for finding triple quoted values on one line
  1.1171 +    _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
  1.1172 +    _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
  1.1173 +    _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
  1.1174 +    _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
  1.1175 +
  1.1176 +    _triple_quote = {
  1.1177 +        "'''": (_single_line_single, _multi_line_single),
  1.1178 +        '"""': (_single_line_double, _multi_line_double),
  1.1179 +    }
  1.1180 +
  1.1181 +    # Used by the ``istrue`` Section method
  1.1182 +    _bools = {
  1.1183 +        'yes': True, 'no': False,
  1.1184 +        'on': True, 'off': False,
  1.1185 +        '1': True, '0': False,
  1.1186 +        'true': True, 'false': False,
  1.1187 +        }
  1.1188 +
  1.1189 +
  1.1190 +    def __init__(self, infile=None, options=None, configspec=None, encoding=None,
  1.1191 +                 interpolation=True, raise_errors=False, list_values=True,
  1.1192 +                 create_empty=False, file_error=False, stringify=True,
  1.1193 +                 indent_type=None, default_encoding=None, unrepr=False,
  1.1194 +                 write_empty_values=False, _inspec=False):
  1.1195 +        """
  1.1196 +        Parse a config file or create a config file object.
  1.1197 +        
  1.1198 +        ``ConfigObj(infile=None, configspec=None, encoding=None,
  1.1199 +                    interpolation=True, raise_errors=False, list_values=True,
  1.1200 +                    create_empty=False, file_error=False, stringify=True,
  1.1201 +                    indent_type=None, default_encoding=None, unrepr=False,
  1.1202 +                    write_empty_values=False, _inspec=False)``
  1.1203 +        """
  1.1204 +        self._inspec = _inspec
  1.1205 +        # init the superclass
  1.1206 +        Section.__init__(self, self, 0, self)
  1.1207 +        
  1.1208 +        infile = infile or []
  1.1209 +        
  1.1210 +        _options = {'configspec': configspec,
  1.1211 +                    'encoding': encoding, 'interpolation': interpolation,
  1.1212 +                    'raise_errors': raise_errors, 'list_values': list_values,
  1.1213 +                    'create_empty': create_empty, 'file_error': file_error,
  1.1214 +                    'stringify': stringify, 'indent_type': indent_type,
  1.1215 +                    'default_encoding': default_encoding, 'unrepr': unrepr,
  1.1216 +                    'write_empty_values': write_empty_values}
  1.1217 +
  1.1218 +        if options is None:
  1.1219 +            options = _options
  1.1220 +        else:
  1.1221 +            import warnings
  1.1222 +            warnings.warn('Passing in an options dictionary to ConfigObj() is '
  1.1223 +                          'deprecated. Use **options instead.',
  1.1224 +                          DeprecationWarning, stacklevel=2)
  1.1225 +            
  1.1226 +            # TODO: check the values too.
  1.1227 +            for entry in options:
  1.1228 +                if entry not in OPTION_DEFAULTS:
  1.1229 +                    raise TypeError('Unrecognised option "%s".' % entry)
  1.1230 +            for entry, value in OPTION_DEFAULTS.items():
  1.1231 +                if entry not in options:
  1.1232 +                    options[entry] = value
  1.1233 +                keyword_value = _options[entry]
  1.1234 +                if value != keyword_value:
  1.1235 +                    options[entry] = keyword_value
  1.1236 +        
  1.1237 +        # XXXX this ignores an explicit list_values = True in combination
  1.1238 +        # with _inspec. The user should *never* do that anyway, but still...
  1.1239 +        if _inspec:
  1.1240 +            options['list_values'] = False
  1.1241 +        
  1.1242 +        self._initialise(options)
  1.1243 +        configspec = options['configspec']
  1.1244 +        self._original_configspec = configspec
  1.1245 +        self._load(infile, configspec)
  1.1246 +        
  1.1247 +        
  1.1248 +    def _load(self, infile, configspec):
  1.1249 +        if isinstance(infile, basestring):
  1.1250 +            self.filename = infile
  1.1251 +            if os.path.isfile(infile):
  1.1252 +                h = open(infile, 'rb')
  1.1253 +                infile = h.read() or []
  1.1254 +                h.close()
  1.1255 +            elif self.file_error:
  1.1256 +                # raise an error if the file doesn't exist
  1.1257 +                raise IOError('Config file not found: "%s".' % self.filename)
  1.1258 +            else:
  1.1259 +                # file doesn't already exist
  1.1260 +                if self.create_empty:
  1.1261 +                    # this is a good test that the filename specified
  1.1262 +                    # isn't impossible - like on a non-existent device
  1.1263 +                    h = open(infile, 'w')
  1.1264 +                    h.write('')
  1.1265 +                    h.close()
  1.1266 +                infile = []
  1.1267 +                
  1.1268 +        elif isinstance(infile, (list, tuple)):
  1.1269 +            infile = list(infile)
  1.1270 +            
  1.1271 +        elif isinstance(infile, dict):
  1.1272 +            # initialise self
  1.1273 +            # the Section class handles creating subsections
  1.1274 +            if isinstance(infile, ConfigObj):
  1.1275 +                # get a copy of our ConfigObj
  1.1276 +                def set_section(in_section, this_section):
  1.1277 +                    for entry in in_section.scalars:
  1.1278 +                        this_section[entry] = in_section[entry]
  1.1279 +                    for section in in_section.sections:
  1.1280 +                        this_section[section] = {}
  1.1281 +                        set_section(in_section[section], this_section[section])
  1.1282 +                set_section(infile, self)
  1.1283 +                
  1.1284 +            else:
  1.1285 +                for entry in infile:
  1.1286 +                    self[entry] = infile[entry]
  1.1287 +            del self._errors
  1.1288 +            
  1.1289 +            if configspec is not None:
  1.1290 +                self._handle_configspec(configspec)
  1.1291 +            else:
  1.1292 +                self.configspec = None
  1.1293 +            return
  1.1294 +        
  1.1295 +        elif getattr(infile, 'read', MISSING) is not MISSING:
  1.1296 +            # This supports file like objects
  1.1297 +            infile = infile.read() or []
  1.1298 +            # needs splitting into lines - but needs doing *after* decoding
  1.1299 +            # in case it's not an 8 bit encoding
  1.1300 +        else:
  1.1301 +            raise TypeError('infile must be a filename, file like object, or list of lines.')
  1.1302 +        
  1.1303 +        if infile:
  1.1304 +            # don't do it for the empty ConfigObj
  1.1305 +            infile = self._handle_bom(infile)
  1.1306 +            # infile is now *always* a list
  1.1307 +            #
  1.1308 +            # Set the newlines attribute (first line ending it finds)
  1.1309 +            # and strip trailing '\n' or '\r' from lines
  1.1310 +            for line in infile:
  1.1311 +                if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
  1.1312 +                    continue
  1.1313 +                for end in ('\r\n', '\n', '\r'):
  1.1314 +                    if line.endswith(end):
  1.1315 +                        self.newlines = end
  1.1316 +                        break
  1.1317 +                break
  1.1318 +
  1.1319 +            infile = [line.rstrip('\r\n') for line in infile]
  1.1320 +            
  1.1321 +        self._parse(infile)
  1.1322 +        # if we had any errors, now is the time to raise them
  1.1323 +        if self._errors:
  1.1324 +            info = "at line %s." % self._errors[0].line_number
  1.1325 +            if len(self._errors) > 1:
  1.1326 +                msg = "Parsing failed with several errors.\nFirst error %s" % info
  1.1327 +                error = ConfigObjError(msg)
  1.1328 +            else:
  1.1329 +                error = self._errors[0]
  1.1330 +            # set the errors attribute; it's a list of tuples:
  1.1331 +            # (error_type, message, line_number)
  1.1332 +            error.errors = self._errors
  1.1333 +            # set the config attribute
  1.1334 +            error.config = self
  1.1335 +            raise error
  1.1336 +        # delete private attributes
  1.1337 +        del self._errors
  1.1338 +        
  1.1339 +        if configspec is None:
  1.1340 +            self.configspec = None
  1.1341 +        else:
  1.1342 +            self._handle_configspec(configspec)
  1.1343 +    
  1.1344 +    
  1.1345 +    def _initialise(self, options=None):
  1.1346 +        if options is None:
  1.1347 +            options = OPTION_DEFAULTS
  1.1348 +            
  1.1349 +        # initialise a few variables
  1.1350 +        self.filename = None
  1.1351 +        self._errors = []
  1.1352 +        self.raise_errors = options['raise_errors']
  1.1353 +        self.interpolation = options['interpolation']
  1.1354 +        self.list_values = options['list_values']
  1.1355 +        self.create_empty = options['create_empty']
  1.1356 +        self.file_error = options['file_error']
  1.1357 +        self.stringify = options['stringify']
  1.1358 +        self.indent_type = options['indent_type']
  1.1359 +        self.encoding = options['encoding']
  1.1360 +        self.default_encoding = options['default_encoding']
  1.1361 +        self.BOM = False
  1.1362 +        self.newlines = None
  1.1363 +        self.write_empty_values = options['write_empty_values']
  1.1364 +        self.unrepr = options['unrepr']
  1.1365 +        
  1.1366 +        self.initial_comment = []
  1.1367 +        self.final_comment = []
  1.1368 +        self.configspec = None
  1.1369 +        
  1.1370 +        if self._inspec:
  1.1371 +            self.list_values = False
  1.1372 +        
  1.1373 +        # Clear section attributes as well
  1.1374 +        Section._initialise(self)
  1.1375 +        
  1.1376 +        
  1.1377 +    def __repr__(self):
  1.1378 +        def _getval(key):
  1.1379 +            try:
  1.1380 +                return self[key]
  1.1381 +            except MissingInterpolationOption:
  1.1382 +                return dict.__getitem__(self, key)
  1.1383 +        return ('ConfigObj({%s})' % 
  1.1384 +                ', '.join([('%s: %s' % (repr(key), repr(_getval(key)))) 
  1.1385 +                for key in (self.scalars + self.sections)]))
  1.1386 +    
  1.1387 +    
  1.1388 +    def _handle_bom(self, infile):
  1.1389 +        """
  1.1390 +        Handle any BOM, and decode if necessary.
  1.1391 +        
  1.1392 +        If an encoding is specified, that *must* be used - but the BOM should
  1.1393 +        still be removed (and the BOM attribute set).
  1.1394 +        
  1.1395 +        (If the encoding is wrongly specified, then a BOM for an alternative
  1.1396 +        encoding won't be discovered or removed.)
  1.1397 +        
  1.1398 +        If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
  1.1399 +        removed. The BOM attribute will be set. UTF16 will be decoded to
  1.1400 +        unicode.
  1.1401 +        
  1.1402 +        NOTE: This method must not be called with an empty ``infile``.
  1.1403 +        
  1.1404 +        Specifying the *wrong* encoding is likely to cause a
  1.1405 +        ``UnicodeDecodeError``.
  1.1406 +        
  1.1407 +        ``infile`` must always be returned as a list of lines, but may be
  1.1408 +        passed in as a single string.
  1.1409 +        """
  1.1410 +        if ((self.encoding is not None) and
  1.1411 +            (self.encoding.lower() not in BOM_LIST)):
  1.1412 +            # No need to check for a BOM
  1.1413 +            # the encoding specified doesn't have one
  1.1414 +            # just decode
  1.1415 +            return self._decode(infile, self.encoding)
  1.1416 +        
  1.1417 +        if isinstance(infile, (list, tuple)):
  1.1418 +            line = infile[0]
  1.1419 +        else:
  1.1420 +            line = infile
  1.1421 +        if self.encoding is not None:
  1.1422 +            # encoding explicitly supplied
  1.1423 +            # And it could have an associated BOM
  1.1424 +            # TODO: if encoding is just UTF16 - we ought to check for both
  1.1425 +            # TODO: big endian and little endian versions.
  1.1426 +            enc = BOM_LIST[self.encoding.lower()]
  1.1427 +            if enc == 'utf_16':
  1.1428 +                # For UTF16 we try big endian and little endian
  1.1429 +                for BOM, (encoding, final_encoding) in BOMS.items():
  1.1430 +                    if not final_encoding:
  1.1431 +                        # skip UTF8
  1.1432 +                        continue
  1.1433 +                    if infile.startswith(BOM):
  1.1434 +                        ### BOM discovered
  1.1435 +                        ##self.BOM = True
  1.1436 +                        # Don't need to remove BOM
  1.1437 +                        return self._decode(infile, encoding)
  1.1438 +                    
  1.1439 +                # If we get this far, will *probably* raise a DecodeError
  1.1440 +                # As it doesn't appear to start with a BOM
  1.1441 +                return self._decode(infile, self.encoding)
  1.1442 +            
  1.1443 +            # Must be UTF8
  1.1444 +            BOM = BOM_SET[enc]
  1.1445 +            if not line.startswith(BOM):
  1.1446 +                return self._decode(infile, self.encoding)
  1.1447 +            
  1.1448 +            newline = line[len(BOM):]
  1.1449 +            
  1.1450 +            # BOM removed
  1.1451 +            if isinstance(infile, (list, tuple)):
  1.1452 +                infile[0] = newline
  1.1453 +            else:
  1.1454 +                infile = newline
  1.1455 +            self.BOM = True
  1.1456 +            return self._decode(infile, self.encoding)
  1.1457 +        
  1.1458 +        # No encoding specified - so we need to check for UTF8/UTF16
  1.1459 +        for BOM, (encoding, final_encoding) in BOMS.items():
  1.1460 +            if not line.startswith(BOM):
  1.1461 +                continue
  1.1462 +            else:
  1.1463 +                # BOM discovered
  1.1464 +                self.encoding = final_encoding
  1.1465 +                if not final_encoding:
  1.1466 +                    self.BOM = True
  1.1467 +                    # UTF8
  1.1468 +                    # remove BOM
  1.1469 +                    newline = line[len(BOM):]
  1.1470 +                    if isinstance(infile, (list, tuple)):
  1.1471 +                        infile[0] = newline
  1.1472 +                    else:
  1.1473 +                        infile = newline
  1.1474 +                    # UTF8 - don't decode
  1.1475 +                    if isinstance(infile, basestring):
  1.1476 +                        return infile.splitlines(True)
  1.1477 +                    else:
  1.1478 +                        return infile
  1.1479 +                # UTF16 - have to decode
  1.1480 +                return self._decode(infile, encoding)
  1.1481 +            
  1.1482 +        # No BOM discovered and no encoding specified, just return
  1.1483 +        if isinstance(infile, basestring):
  1.1484 +            # infile read from a file will be a single string
  1.1485 +            return infile.splitlines(True)
  1.1486 +        return infile
  1.1487 +
  1.1488 +
  1.1489 +    def _a_to_u(self, aString):
  1.1490 +        """Decode ASCII strings to unicode if a self.encoding is specified."""
  1.1491 +        if self.encoding:
  1.1492 +            return aString.decode('ascii')
  1.1493 +        else:
  1.1494 +            return aString
  1.1495 +
  1.1496 +
  1.1497 +    def _decode(self, infile, encoding):
  1.1498 +        """
  1.1499 +        Decode infile to unicode. Using the specified encoding.
  1.1500 +        
  1.1501 +        if is a string, it also needs converting to a list.
  1.1502 +        """
  1.1503 +        if isinstance(infile, basestring):
  1.1504 +            # can't be unicode
  1.1505 +            # NOTE: Could raise a ``UnicodeDecodeError``
  1.1506 +            return infile.decode(encoding).splitlines(True)
  1.1507 +        for i, line in enumerate(infile):
  1.1508 +            if not isinstance(line, unicode):
  1.1509 +                # NOTE: The isinstance test here handles mixed lists of unicode/string
  1.1510 +                # NOTE: But the decode will break on any non-string values
  1.1511 +                # NOTE: Or could raise a ``UnicodeDecodeError``
  1.1512 +                infile[i] = line.decode(encoding)
  1.1513 +        return infile
  1.1514 +
  1.1515 +
  1.1516 +    def _decode_element(self, line):
  1.1517 +        """Decode element to unicode if necessary."""
  1.1518 +        if not self.encoding:
  1.1519 +            return line
  1.1520 +        if isinstance(line, str) and self.default_encoding:
  1.1521 +            return line.decode(self.default_encoding)
  1.1522 +        return line
  1.1523 +
  1.1524 +
  1.1525 +    def _str(self, value):
  1.1526 +        """
  1.1527 +        Used by ``stringify`` within validate, to turn non-string values
  1.1528 +        into strings.
  1.1529 +        """
  1.1530 +        if not isinstance(value, basestring):
  1.1531 +            return str(value)
  1.1532 +        else:
  1.1533 +            return value
  1.1534 +
  1.1535 +
  1.1536 +    def _parse(self, infile):
  1.1537 +        """Actually parse the config file."""
  1.1538 +        temp_list_values = self.list_values
  1.1539 +        if self.unrepr:
  1.1540 +            self.list_values = False
  1.1541 +            
  1.1542 +        comment_list = []
  1.1543 +        done_start = False
  1.1544 +        this_section = self
  1.1545 +        maxline = len(infile) - 1
  1.1546 +        cur_index = -1
  1.1547 +        reset_comment = False
  1.1548 +        
  1.1549 +        while cur_index < maxline:
  1.1550 +            if reset_comment:
  1.1551 +                comment_list = []
  1.1552 +            cur_index += 1
  1.1553 +            line = infile[cur_index]
  1.1554 +            sline = line.strip()
  1.1555 +            # do we have anything on the line ?
  1.1556 +            if not sline or sline.startswith('#'):
  1.1557 +                reset_comment = False
  1.1558 +                comment_list.append(line)
  1.1559 +                continue
  1.1560 +            
  1.1561 +            if not done_start:
  1.1562 +                # preserve initial comment
  1.1563 +                self.initial_comment = comment_list
  1.1564 +                comment_list = []
  1.1565 +                done_start = True
  1.1566 +                
  1.1567 +            reset_comment = True
  1.1568 +            # first we check if it's a section marker
  1.1569 +            mat = self._sectionmarker.match(line)
  1.1570 +            if mat is not None:
  1.1571 +                # is a section line
  1.1572 +                (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
  1.1573 +                if indent and (self.indent_type is None):
  1.1574 +                    self.indent_type = indent
  1.1575 +                cur_depth = sect_open.count('[')
  1.1576 +                if cur_depth != sect_close.count(']'):
  1.1577 +                    self._handle_error("Cannot compute the section depth at line %s.",
  1.1578 +                                       NestingError, infile, cur_index)
  1.1579 +                    continue
  1.1580 +                
  1.1581 +                if cur_depth < this_section.depth:
  1.1582 +                    # the new section is dropping back to a previous level
  1.1583 +                    try:
  1.1584 +                        parent = self._match_depth(this_section,
  1.1585 +                                                   cur_depth).parent
  1.1586 +                    except SyntaxError:
  1.1587 +                        self._handle_error("Cannot compute nesting level at line %s.",
  1.1588 +                                           NestingError, infile, cur_index)
  1.1589 +                        continue
  1.1590 +                elif cur_depth == this_section.depth:
  1.1591 +                    # the new section is a sibling of the current section
  1.1592 +                    parent = this_section.parent
  1.1593 +                elif cur_depth == this_section.depth + 1:
  1.1594 +                    # the new section is a child the current section
  1.1595 +                    parent = this_section
  1.1596 +                else:
  1.1597 +                    self._handle_error("Section too nested at line %s.",
  1.1598 +                                       NestingError, infile, cur_index)
  1.1599 +                    
  1.1600 +                sect_name = self._unquote(sect_name)
  1.1601 +                if sect_name in parent:
  1.1602 +                    self._handle_error('Duplicate section name at line %s.',
  1.1603 +                                       DuplicateError, infile, cur_index)
  1.1604 +                    continue
  1.1605 +                
  1.1606 +                # create the new section
  1.1607 +                this_section = Section(
  1.1608 +                    parent,
  1.1609 +                    cur_depth,
  1.1610 +                    self,
  1.1611 +                    name=sect_name)
  1.1612 +                parent[sect_name] = this_section
  1.1613 +                parent.inline_comments[sect_name] = comment
  1.1614 +                parent.comments[sect_name] = comment_list
  1.1615 +                continue
  1.1616 +            #
  1.1617 +            # it's not a section marker,
  1.1618 +            # so it should be a valid ``key = value`` line
  1.1619 +            mat = self._keyword.match(line)
  1.1620 +            if mat is None:
  1.1621 +                # it neither matched as a keyword
  1.1622 +                # or a section marker
  1.1623 +                self._handle_error(
  1.1624 +                    'Invalid line at line "%s".',
  1.1625 +                    ParseError, infile, cur_index)
  1.1626 +            else:
  1.1627 +                # is a keyword value
  1.1628 +                # value will include any inline comment
  1.1629 +                (indent, key, value) = mat.groups()
  1.1630 +                if indent and (self.indent_type is None):
  1.1631 +                    self.indent_type = indent
  1.1632 +                # check for a multiline value
  1.1633 +                if value[:3] in ['"""', "'''"]:
  1.1634 +                    try:
  1.1635 +                        value, comment, cur_index = self._multiline(
  1.1636 +                            value, infile, cur_index, maxline)
  1.1637 +                    except SyntaxError:
  1.1638 +                        self._handle_error(
  1.1639 +                            'Parse error in value at line %s.',
  1.1640 +                            ParseError, infile, cur_index)
  1.1641 +                        continue
  1.1642 +                    else:
  1.1643 +                        if self.unrepr:
  1.1644 +                            comment = ''
  1.1645 +                            try:
  1.1646 +                                value = unrepr(value)
  1.1647 +                            except Exception, e:
  1.1648 +                                if type(e) == UnknownType:
  1.1649 +                                    msg = 'Unknown name or type in value at line %s.'
  1.1650 +                                else:
  1.1651 +                                    msg = 'Parse error in value at line %s.'
  1.1652 +                                self._handle_error(msg, UnreprError, infile,
  1.1653 +                                    cur_index)
  1.1654 +                                continue
  1.1655 +                else:
  1.1656 +                    if self.unrepr:
  1.1657 +                        comment = ''
  1.1658 +                        try:
  1.1659 +                            value = unrepr(value)
  1.1660 +                        except Exception, e:
  1.1661 +                            if isinstance(e, UnknownType):
  1.1662 +                                msg = 'Unknown name or type in value at line %s.'
  1.1663 +                            else:
  1.1664 +                                msg = 'Parse error in value at line %s.'
  1.1665 +                            self._handle_error(msg, UnreprError, infile,
  1.1666 +                                cur_index)
  1.1667 +                            continue
  1.1668 +                    else:
  1.1669 +                        # extract comment and lists
  1.1670 +                        try:
  1.1671 +                            (value, comment) = self._handle_value(value)
  1.1672 +                        except SyntaxError:
  1.1673 +                            self._handle_error(
  1.1674 +                                'Parse error in value at line %s.',
  1.1675 +                                ParseError, infile, cur_index)
  1.1676 +                            continue
  1.1677 +                #
  1.1678 +                key = self._unquote(key)
  1.1679 +                if key in this_section:
  1.1680 +                    self._handle_error(
  1.1681 +                        'Duplicate keyword name at line %s.',
  1.1682 +                        DuplicateError, infile, cur_index)
  1.1683 +                    continue
  1.1684 +                # add the key.
  1.1685 +                # we set unrepr because if we have got this far we will never
  1.1686 +                # be creating a new section
  1.1687 +                this_section.__setitem__(key, value, unrepr=True)
  1.1688 +                this_section.inline_comments[key] = comment
  1.1689 +                this_section.comments[key] = comment_list
  1.1690 +                continue
  1.1691 +        #
  1.1692 +        if self.indent_type is None:
  1.1693 +            # no indentation used, set the type accordingly
  1.1694 +            self.indent_type = ''
  1.1695 +
  1.1696 +        # preserve the final comment
  1.1697 +        if not self and not self.initial_comment:
  1.1698 +            self.initial_comment = comment_list
  1.1699 +        elif not reset_comment:
  1.1700 +            self.final_comment = comment_list
  1.1701 +        self.list_values = temp_list_values
  1.1702 +
  1.1703 +
  1.1704 +    def _match_depth(self, sect, depth):
  1.1705 +        """
  1.1706 +        Given a section and a depth level, walk back through the sections
  1.1707 +        parents to see if the depth level matches a previous section.
  1.1708 +        
  1.1709 +        Return a reference to the right section,
  1.1710 +        or raise a SyntaxError.
  1.1711 +        """
  1.1712 +        while depth < sect.depth:
  1.1713 +            if sect is sect.parent:
  1.1714 +                # we've reached the top level already
  1.1715 +                raise SyntaxError()
  1.1716 +            sect = sect.parent
  1.1717 +        if sect.depth == depth:
  1.1718 +            return sect
  1.1719 +        # shouldn't get here
  1.1720 +        raise SyntaxError()
  1.1721 +
  1.1722 +
  1.1723 +    def _handle_error(self, text, ErrorClass, infile, cur_index):
  1.1724 +        """
  1.1725 +        Handle an error according to the error settings.
  1.1726 +        
  1.1727 +        Either raise the error or store it.
  1.1728 +        The error will have occured at ``cur_index``
  1.1729 +        """
  1.1730 +        line = infile[cur_index]
  1.1731 +        cur_index += 1
  1.1732 +        message = text % cur_index
  1.1733 +        error = ErrorClass(message, cur_index, line)
  1.1734 +        if self.raise_errors:
  1.1735 +            # raise the error - parsing stops here
  1.1736 +            raise error
  1.1737 +        # store the error
  1.1738 +        # reraise when parsing has finished
  1.1739 +        self._errors.append(error)
  1.1740 +
  1.1741 +
  1.1742 +    def _unquote(self, value):
  1.1743 +        """Return an unquoted version of a value"""
  1.1744 +        if not value:
  1.1745 +            # should only happen during parsing of lists
  1.1746 +            raise SyntaxError
  1.1747 +        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
  1.1748 +            value = value[1:-1]
  1.1749 +        return value
  1.1750 +
  1.1751 +
  1.1752 +    def _quote(self, value, multiline=True):
  1.1753 +        """
  1.1754 +        Return a safely quoted version of a value.
  1.1755 +        
  1.1756 +        Raise a ConfigObjError if the value cannot be safely quoted.
  1.1757 +        If multiline is ``True`` (default) then use triple quotes
  1.1758 +        if necessary.
  1.1759 +        
  1.1760 +        * Don't quote values that don't need it.
  1.1761 +        * Recursively quote members of a list and return a comma joined list.
  1.1762 +        * Multiline is ``False`` for lists.
  1.1763 +        * Obey list syntax for empty and single member lists.
  1.1764 +        
  1.1765 +        If ``list_values=False`` then the value is only quoted if it contains
  1.1766 +        a ``\\n`` (is multiline) or '#'.
  1.1767 +        
  1.1768 +        If ``write_empty_values`` is set, and the value is an empty string, it
  1.1769 +        won't be quoted.
  1.1770 +        """
  1.1771 +        if multiline and self.write_empty_values and value == '':
  1.1772 +            # Only if multiline is set, so that it is used for values not
  1.1773 +            # keys, and not values that are part of a list
  1.1774 +            return ''
  1.1775 +        
  1.1776 +        if multiline and isinstance(value, (list, tuple)):
  1.1777 +            if not value:
  1.1778 +                return ','
  1.1779 +            elif len(value) == 1:
  1.1780 +                return self._quote(value[0], multiline=False) + ','
  1.1781 +            return ', '.join([self._quote(val, multiline=False)
  1.1782 +                for val in value])
  1.1783 +        if not isinstance(value, basestring):
  1.1784 +            if self.stringify:
  1.1785 +                value = str(value)
  1.1786 +            else:
  1.1787 +                raise TypeError('Value "%s" is not a string.' % value)
  1.1788 +
  1.1789 +        if not value:
  1.1790 +            return '""'
  1.1791 +        
  1.1792 +        no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
  1.1793 +        need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
  1.1794 +        hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
  1.1795 +        check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
  1.1796 +        
  1.1797 +        if check_for_single:
  1.1798 +            if not self.list_values:
  1.1799 +                # we don't quote if ``list_values=False``
  1.1800 +                quot = noquot
  1.1801 +            # for normal values either single or double quotes will do
  1.1802 +            elif '\n' in value:
  1.1803 +                # will only happen if multiline is off - e.g. '\n' in key
  1.1804 +                raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
  1.1805 +            elif ((value[0] not in wspace_plus) and
  1.1806 +                    (value[-1] not in wspace_plus) and
  1.1807 +                    (',' not in value)):
  1.1808 +                quot = noquot
  1.1809 +            else:
  1.1810 +                quot = self._get_single_quote(value)
  1.1811 +        else:
  1.1812 +            # if value has '\n' or "'" *and* '"', it will need triple quotes
  1.1813 +            quot = self._get_triple_quote(value)
  1.1814 +        
  1.1815 +        if quot == noquot and '#' in value and self.list_values:
  1.1816 +            quot = self._get_single_quote(value)
  1.1817 +                
  1.1818 +        return quot % value
  1.1819 +    
  1.1820 +    
  1.1821 +    def _get_single_quote(self, value):
  1.1822 +        if ("'" in value) and ('"' in value):
  1.1823 +            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
  1.1824 +        elif '"' in value:
  1.1825 +            quot = squot
  1.1826 +        else:
  1.1827 +            quot = dquot
  1.1828 +        return quot
  1.1829 +    
  1.1830 +    
  1.1831 +    def _get_triple_quote(self, value):
  1.1832 +        if (value.find('"""') != -1) and (value.find("'''") != -1):
  1.1833 +            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
  1.1834 +        if value.find('"""') == -1:
  1.1835 +            quot = tdquot
  1.1836 +        else:
  1.1837 +            quot = tsquot 
  1.1838 +        return quot
  1.1839 +
  1.1840 +
  1.1841 +    def _handle_value(self, value):
  1.1842 +        """
  1.1843 +        Given a value string, unquote, remove comment,
  1.1844 +        handle lists. (including empty and single member lists)
  1.1845 +        """
  1.1846 +        if self._inspec:
  1.1847 +            # Parsing a configspec so don't handle comments
  1.1848 +            return (value, '')
  1.1849 +        # do we look for lists in values ?
  1.1850 +        if not self.list_values:
  1.1851 +            mat = self._nolistvalue.match(value)
  1.1852 +            if mat is None:
  1.1853 +                raise SyntaxError()
  1.1854 +            # NOTE: we don't unquote here
  1.1855 +            return mat.groups()
  1.1856 +        #
  1.1857 +        mat = self._valueexp.match(value)
  1.1858 +        if mat is None:
  1.1859 +            # the value is badly constructed, probably badly quoted,
  1.1860 +            # or an invalid list
  1.1861 +            raise SyntaxError()
  1.1862 +        (list_values, single, empty_list, comment) = mat.groups()
  1.1863 +        if (list_values == '') and (single is None):
  1.1864 +            # change this if you want to accept empty values
  1.1865 +            raise SyntaxError()
  1.1866 +        # NOTE: note there is no error handling from here if the regex
  1.1867 +        # is wrong: then incorrect values will slip through
  1.1868 +        if empty_list is not None:
  1.1869 +            # the single comma - meaning an empty list
  1.1870 +            return ([], comment)
  1.1871 +        if single is not None:
  1.1872 +            # handle empty values
  1.1873 +            if list_values and not single:
  1.1874 +                # FIXME: the '' is a workaround because our regex now matches
  1.1875 +                #   '' at the end of a list if it has a trailing comma
  1.1876 +                single = None
  1.1877 +            else:
  1.1878 +                single = single or '""'
  1.1879 +                single = self._unquote(single)
  1.1880 +        if list_values == '':
  1.1881 +            # not a list value
  1.1882 +            return (single, comment)
  1.1883 +        the_list = self._listvalueexp.findall(list_values)
  1.1884 +        the_list = [self._unquote(val) for val in the_list]
  1.1885 +        if single is not None:
  1.1886 +            the_list += [single]
  1.1887 +        return (the_list, comment)
  1.1888 +
  1.1889 +
  1.1890 +    def _multiline(self, value, infile, cur_index, maxline):
  1.1891 +        """Extract the value, where we are in a multiline situation."""
  1.1892 +        quot = value[:3]
  1.1893 +        newvalue = value[3:]
  1.1894 +        single_line = self._triple_quote[quot][0]
  1.1895 +        multi_line = self._triple_quote[quot][1]
  1.1896 +        mat = single_line.match(value)
  1.1897 +        if mat is not None:
  1.1898 +            retval = list(mat.groups())
  1.1899 +            retval.append(cur_index)
  1.1900 +            return retval
  1.1901 +        elif newvalue.find(quot) != -1:
  1.1902 +            # somehow the triple quote is missing
  1.1903 +            raise SyntaxError()
  1.1904 +        #
  1.1905 +        while cur_index < maxline:
  1.1906 +            cur_index += 1
  1.1907 +            newvalue += '\n'
  1.1908 +            line = infile[cur_index]
  1.1909 +            if line.find(quot) == -1:
  1.1910 +                newvalue += line
  1.1911 +            else:
  1.1912 +                # end of multiline, process it
  1.1913 +                break
  1.1914 +        else:
  1.1915 +            # we've got to the end of the config, oops...
  1.1916 +            raise SyntaxError()
  1.1917 +        mat = multi_line.match(line)
  1.1918 +        if mat is None:
  1.1919 +            # a badly formed line
  1.1920 +            raise SyntaxError()
  1.1921 +        (value, comment) = mat.groups()
  1.1922 +        return (newvalue + value, comment, cur_index)
  1.1923 +
  1.1924 +
  1.1925 +    def _handle_configspec(self, configspec):
  1.1926 +        """Parse the configspec."""
  1.1927 +        # FIXME: Should we check that the configspec was created with the 
  1.1928 +        #        correct settings ? (i.e. ``list_values=False``)
  1.1929 +        if not isinstance(configspec, ConfigObj):
  1.1930 +            try:
  1.1931 +                configspec = ConfigObj(configspec,
  1.1932 +                                       raise_errors=True,
  1.1933 +                                       file_error=True,
  1.1934 +                                       _inspec=True)
  1.1935 +            except ConfigObjError, e:
  1.1936 +                # FIXME: Should these errors have a reference
  1.1937 +                #        to the already parsed ConfigObj ?
  1.1938 +                raise ConfigspecError('Parsing configspec failed: %s' % e)
  1.1939 +            except IOError, e:
  1.1940 +                raise IOError('Reading configspec failed: %s' % e)
  1.1941 +        
  1.1942 +        self.configspec = configspec
  1.1943 +            
  1.1944 +
  1.1945 +        
  1.1946 +    def _set_configspec(self, section, copy):
  1.1947 +        """
  1.1948 +        Called by validate. Handles setting the configspec on subsections
  1.1949 +        including sections to be validated by __many__
  1.1950 +        """
  1.1951 +        configspec = section.configspec
  1.1952 +        many = configspec.get('__many__')
  1.1953 +        if isinstance(many, dict):
  1.1954 +            for entry in section.sections:
  1.1955 +                if entry not in configspec:
  1.1956 +                    section[entry].configspec = many
  1.1957 +                    
  1.1958 +        for entry in configspec.sections:
  1.1959 +            if entry == '__many__':
  1.1960 +                continue
  1.1961 +            if entry not in section:
  1.1962 +                section[entry] = {}
  1.1963 +                section[entry]._created = True
  1.1964 +                if copy:
  1.1965 +                    # copy comments
  1.1966 +                    section.comments[entry] = configspec.comments.get(entry, [])
  1.1967 +                    section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
  1.1968 +                
  1.1969 +            # Could be a scalar when we expect a section
  1.1970 +            if isinstance(section[entry], Section):
  1.1971 +                section[entry].configspec = configspec[entry]
  1.1972 +                        
  1.1973 +
  1.1974 +    def _write_line(self, indent_string, entry, this_entry, comment):
  1.1975 +        """Write an individual line, for the write method"""
  1.1976 +        # NOTE: the calls to self._quote here handles non-StringType values.
  1.1977 +        if not self.unrepr:
  1.1978 +            val = self._decode_element(self._quote(this_entry))
  1.1979 +        else:
  1.1980 +            val = repr(this_entry)
  1.1981 +        return '%s%s%s%s%s' % (indent_string,
  1.1982 +                               self._decode_element(self._quote(entry, multiline=False)),
  1.1983 +                               self._a_to_u(' = '),
  1.1984 +                               val,
  1.1985 +                               self._decode_element(comment))
  1.1986 +
  1.1987 +
  1.1988 +    def _write_marker(self, indent_string, depth, entry, comment):
  1.1989 +        """Write a section marker line"""
  1.1990 +        return '%s%s%s%s%s' % (indent_string,
  1.1991 +                               self._a_to_u('[' * depth),
  1.1992 +                               self._quote(self._decode_element(entry), multiline=False),
  1.1993 +                               self._a_to_u(']' * depth),
  1.1994 +                               self._decode_element(comment))
  1.1995 +
  1.1996 +
  1.1997 +    def _handle_comment(self, comment):
  1.1998 +        """Deal with a comment."""
  1.1999 +        if not comment:
  1.2000 +            return ''
  1.2001 +        start = self.indent_type
  1.2002 +        if not comment.startswith('#'):
  1.2003 +            start += self._a_to_u(' # ')
  1.2004 +        return (start + comment)
  1.2005 +
  1.2006 +
  1.2007 +    # Public methods
  1.2008 +
  1.2009 +    def write(self, outfile=None, section=None):
  1.2010 +        """
  1.2011 +        Write the current ConfigObj as a file
  1.2012 +        
  1.2013 +        tekNico: FIXME: use StringIO instead of real files
  1.2014 +        
  1.2015 +        >>> filename = a.filename
  1.2016 +        >>> a.filename = 'test.ini'
  1.2017 +        >>> a.write()
  1.2018 +        >>> a.filename = filename
  1.2019 +        >>> a == ConfigObj('test.ini', raise_errors=True)
  1.2020 +        1
  1.2021 +        >>> import os
  1.2022 +        >>> os.remove('test.ini')
  1.2023 +        """
  1.2024 +        if self.indent_type is None:
  1.2025 +            # this can be true if initialised from a dictionary
  1.2026 +            self.indent_type = DEFAULT_INDENT_TYPE
  1.2027 +            
  1.2028 +        out = []
  1.2029 +        cs = self._a_to_u('#')
  1.2030 +        csp = self._a_to_u('# ')
  1.2031 +        if section is None:
  1.2032 +            int_val = self.interpolation
  1.2033 +            self.interpolation = False
  1.2034 +            section = self
  1.2035 +            for line in self.initial_comment:
  1.2036 +                line = self._decode_element(line)
  1.2037 +                stripped_line = line.strip()
  1.2038 +                if stripped_line and not stripped_line.startswith(cs):
  1.2039 +                    line = csp + line
  1.2040 +                out.append(line)
  1.2041 +                
  1.2042 +        indent_string = self.indent_type * section.depth
  1.2043 +        for entry in (section.scalars + section.sections):
  1.2044 +            if entry in section.defaults:
  1.2045 +                # don't write out default values
  1.2046 +                continue
  1.2047 +            for comment_line in section.comments[entry]:
  1.2048 +                comment_line = self._decode_element(comment_line.lstrip())
  1.2049 +                if comment_line and not comment_line.startswith(cs):
  1.2050 +                    comment_line = csp + comment_line
  1.2051 +                out.append(indent_string + comment_line)
  1.2052 +            this_entry = section[entry]
  1.2053 +            comment = self._handle_comment(section.inline_comments[entry])
  1.2054 +            
  1.2055 +            if isinstance(this_entry, dict):
  1.2056 +                # a section
  1.2057 +                out.append(self._write_marker(
  1.2058 +                    indent_string,
  1.2059 +                    this_entry.depth,
  1.2060 +                    entry,
  1.2061 +                    comment))
  1.2062 +                out.extend(self.write(section=this_entry))
  1.2063 +            else:
  1.2064 +                out.append(self._write_line(
  1.2065 +                    indent_string,
  1.2066 +                    entry,
  1.2067 +                    this_entry,
  1.2068 +                    comment))
  1.2069 +                
  1.2070 +        if section is self:
  1.2071 +            for line in self.final_comment:
  1.2072 +                line = self._decode_element(line)
  1.2073 +                stripped_line = line.strip()
  1.2074 +                if stripped_line and not stripped_line.startswith(cs):
  1.2075 +                    line = csp + line
  1.2076 +                out.append(line)
  1.2077 +            self.interpolation = int_val
  1.2078 +            
  1.2079 +        if section is not self:
  1.2080 +            return out
  1.2081 +        
  1.2082 +        if (self.filename is None) and (outfile is None):
  1.2083 +            # output a list of lines
  1.2084 +            # might need to encode
  1.2085 +            # NOTE: This will *screw* UTF16, each line will start with the BOM
  1.2086 +            if self.encoding:
  1.2087 +                out = [l.encode(self.encoding) for l in out]
  1.2088 +            if (self.BOM and ((self.encoding is None) or
  1.2089 +                (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
  1.2090 +                # Add the UTF8 BOM
  1.2091 +                if not out:
  1.2092 +                    out.append('')
  1.2093 +                out[0] = BOM_UTF8 + out[0]
  1.2094 +            return out
  1.2095 +        
  1.2096 +        # Turn the list to a string, joined with correct newlines
  1.2097 +        newline = self.newlines or os.linesep
  1.2098 +        if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w'
  1.2099 +            and sys.platform == 'win32' and newline == '\r\n'):
  1.2100 +            # Windows specific hack to avoid writing '\r\r\n'
  1.2101 +            newline = '\n'
  1.2102 +        output = self._a_to_u(newline).join(out)
  1.2103 +        if self.encoding:
  1.2104 +            output = output.encode(self.encoding)
  1.2105 +        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
  1.2106 +            # Add the UTF8 BOM
  1.2107 +            output = BOM_UTF8 + output
  1.2108 +            
  1.2109 +        if not output.endswith(newline):
  1.2110 +            output += newline
  1.2111 +        if outfile is not None:
  1.2112 +            outfile.write(output)
  1.2113 +        else:
  1.2114 +            h = open(self.filename, 'wb')
  1.2115 +            h.write(output)
  1.2116 +            h.close()
  1.2117 +
  1.2118 +
  1.2119 +    def validate(self, validator, preserve_errors=False, copy=False,
  1.2120 +                 section=None):
  1.2121 +        """
  1.2122 +        Test the ConfigObj against a configspec.
  1.2123 +        
  1.2124 +        It uses the ``validator`` object from *validate.py*.
  1.2125 +        
  1.2126 +        To run ``validate`` on the current ConfigObj, call: ::
  1.2127 +        
  1.2128 +            test = config.validate(validator)
  1.2129 +        
  1.2130 +        (Normally having previously passed in the configspec when the ConfigObj
  1.2131 +        was created - you can dynamically assign a dictionary of checks to the
  1.2132 +        ``configspec`` attribute of a section though).
  1.2133 +        
  1.2134 +        It returns ``True`` if everything passes, or a dictionary of
  1.2135 +        pass/fails (True/False). If every member of a subsection passes, it
  1.2136 +        will just have the value ``True``. (It also returns ``False`` if all
  1.2137 +        members fail).
  1.2138 +        
  1.2139 +        In addition, it converts the values from strings to their native
  1.2140 +        types if their checks pass (and ``stringify`` is set).
  1.2141 +        
  1.2142 +        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
  1.2143 +        of a marking a fail with a ``False``, it will preserve the actual
  1.2144 +        exception object. This can contain info about the reason for failure.
  1.2145 +        For example the ``VdtValueTooSmallError`` indicates that the value
  1.2146 +        supplied was too small. If a value (or section) is missing it will
  1.2147 +        still be marked as ``False``.
  1.2148 +        
  1.2149 +        You must have the validate module to use ``preserve_errors=True``.
  1.2150 +        
  1.2151 +        You can then use the ``flatten_errors`` function to turn your nested
  1.2152 +        results dictionary into a flattened list of failures - useful for
  1.2153 +        displaying meaningful error messages.
  1.2154 +        """
  1.2155 +        if section is None:
  1.2156 +            if self.configspec is None:
  1.2157 +                raise ValueError('No configspec supplied.')
  1.2158 +            if preserve_errors:
  1.2159 +                # We do this once to remove a top level dependency on the validate module
  1.2160 +                # Which makes importing configobj faster
  1.2161 +                from validate import VdtMissingValue
  1.2162 +                self._vdtMissingValue = VdtMissingValue
  1.2163 +                
  1.2164 +            section = self
  1.2165 +
  1.2166 +            if copy:
  1.2167 +                section.initial_comment = section.configspec.initial_comment
  1.2168 +                section.final_comment = section.configspec.final_comment
  1.2169 +                section.encoding = section.configspec.encoding
  1.2170 +                section.BOM = section.configspec.BOM
  1.2171 +                section.newlines = section.configspec.newlines
  1.2172 +                section.indent_type = section.configspec.indent_type
  1.2173 +            
  1.2174 +        #
  1.2175 +        # section.default_values.clear() #??
  1.2176 +        configspec = section.configspec
  1.2177 +        self._set_configspec(section, copy)
  1.2178 +
  1.2179 +        
  1.2180 +        def validate_entry(entry, spec, val, missing, ret_true, ret_false):
  1.2181 +            section.default_values.pop(entry, None)
  1.2182 +                
  1.2183 +            try:
  1.2184 +                section.default_values[entry] = validator.get_default_value(configspec[entry])
  1.2185 +            except (KeyError, AttributeError, validator.baseErrorClass):
  1.2186 +                # No default, bad default or validator has no 'get_default_value'
  1.2187 +                # (e.g. SimpleVal)
  1.2188 +                pass
  1.2189 +            
  1.2190 +            try:
  1.2191 +                check = validator.check(spec,
  1.2192 +                                        val,
  1.2193 +                                        missing=missing
  1.2194 +                                        )
  1.2195 +            except validator.baseErrorClass, e:
  1.2196 +                if not preserve_errors or isinstance(e, self._vdtMissingValue):
  1.2197 +                    out[entry] = False
  1.2198 +                else:
  1.2199 +                    # preserve the error
  1.2200 +                    out[entry] = e
  1.2201 +                    ret_false = False
  1.2202 +                ret_true = False
  1.2203 +            else:
  1.2204 +                ret_false = False
  1.2205 +                out[entry] = True
  1.2206 +                if self.stringify or missing:
  1.2207 +                    # if we are doing type conversion
  1.2208 +                    # or the value is a supplied default
  1.2209 +                    if not self.stringify:
  1.2210 +                        if isinstance(check, (list, tuple)):
  1.2211 +                            # preserve lists
  1.2212 +                            check = [self._str(item) for item in check]
  1.2213 +                        elif missing and check is None:
  1.2214 +                            # convert the None from a default to a ''
  1.2215 +                            check = ''
  1.2216 +                        else:
  1.2217 +                            check = self._str(check)
  1.2218 +                    if (check != val) or missing:
  1.2219 +                        section[entry] = check
  1.2220 +                if not copy and missing and entry not in section.defaults:
  1.2221 +                    section.defaults.append(entry)
  1.2222 +            return ret_true, ret_false
  1.2223 +        
  1.2224 +        #
  1.2225 +        out = {}
  1.2226 +        ret_true = True
  1.2227 +        ret_false = True
  1.2228 +        
  1.2229 +        unvalidated = [k for k in section.scalars if k not in configspec]
  1.2230 +        incorrect_sections = [k for k in configspec.sections if k in section.scalars]        
  1.2231 +        incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
  1.2232 +        
  1.2233 +        for entry in configspec.scalars:
  1.2234 +            if entry in ('__many__', '___many___'):
  1.2235 +                # reserved names
  1.2236 +                continue
  1.2237 +            if (not entry in section.scalars) or (entry in section.defaults):
  1.2238 +                # missing entries
  1.2239 +                # or entries from defaults
  1.2240 +                missing = True
  1.2241 +                val = None
  1.2242 +                if copy and entry not in section.scalars:
  1.2243 +                    # copy comments
  1.2244 +                    section.comments[entry] = (
  1.2245 +                        configspec.comments.get(entry, []))
  1.2246 +                    section.inline_comments[entry] = (
  1.2247 +                        configspec.inline_comments.get(entry, ''))
  1.2248 +                #
  1.2249 +            else:
  1.2250 +                missing = False
  1.2251 +                val = section[entry]
  1.2252 +            
  1.2253 +            ret_true, ret_false = validate_entry(entry, configspec[entry], val, 
  1.2254 +                                                 missing, ret_true, ret_false)
  1.2255 +        
  1.2256 +        many = None
  1.2257 +        if '__many__' in configspec.scalars:
  1.2258 +            many = configspec['__many__']
  1.2259 +        elif '___many___' in configspec.scalars:
  1.2260 +            many = configspec['___many___']
  1.2261 +        
  1.2262 +        if many is not None:
  1.2263 +            for entry in unvalidated:
  1.2264 +                val = section[entry]
  1.2265 +                ret_true, ret_false = validate_entry(entry, many, val, False,
  1.2266 +                                                     ret_true, ret_false)
  1.2267 +            unvalidated = []
  1.2268 +
  1.2269 +        for entry in incorrect_scalars:
  1.2270 +            ret_true = False
  1.2271 +            if not preserve_errors:
  1.2272 +                out[entry] = False
  1.2273 +            else:
  1.2274 +                ret_false = False
  1.2275 +                msg = 'Value %r was provided as a section' % entry
  1.2276 +                out[entry] = validator.baseErrorClass(msg)
  1.2277 +        for entry in incorrect_sections:
  1.2278 +            ret_true = False
  1.2279 +            if not preserve_errors:
  1.2280 +                out[entry] = False
  1.2281 +            else:
  1.2282 +                ret_false = False
  1.2283 +                msg = 'Section %r was provided as a single value' % entry
  1.2284 +                out[entry] = validator.baseErrorClass(msg)
  1.2285 +                
  1.2286 +        # Missing sections will have been created as empty ones when the
  1.2287 +        # configspec was read.
  1.2288 +        for entry in section.sections:
  1.2289 +            # FIXME: this means DEFAULT is not copied in copy mode
  1.2290 +            if section is self and entry == 'DEFAULT':
  1.2291 +                continue
  1.2292 +            if section[entry].configspec is None:
  1.2293 +                unvalidated.append(entry)
  1.2294 +                continue
  1.2295 +            if copy:
  1.2296 +                section.comments[entry] = configspec.comments.get(entry, [])
  1.2297 +                section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
  1.2298 +            check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
  1.2299 +            out[entry] = check
  1.2300 +            if check == False:
  1.2301 +                ret_true = False
  1.2302 +            elif check == True:
  1.2303 +                ret_false = False
  1.2304 +            else:
  1.2305 +                ret_true = False
  1.2306 +        
  1.2307 +        section.extra_values = unvalidated
  1.2308 +        if preserve_errors and not section._created:
  1.2309 +            # If the section wasn't created (i.e. it wasn't missing)
  1.2310 +            # then we can't return False, we need to preserve errors
  1.2311 +            ret_false = False
  1.2312 +        #
  1.2313 +        if ret_false and preserve_errors and out:
  1.2314 +            # If we are preserving errors, but all
  1.2315 +            # the failures are from missing sections / values
  1.2316 +            # then we can return False. Otherwise there is a
  1.2317 +            # real failure that we need to preserve.
  1.2318 +            ret_false = not any(out.values())
  1.2319 +        if ret_true:
  1.2320 +            return True
  1.2321 +        elif ret_false:
  1.2322 +            return False
  1.2323 +        return out
  1.2324 +
  1.2325 +
  1.2326 +    def reset(self):
  1.2327 +        """Clear ConfigObj instance and restore to 'freshly created' state."""
  1.2328 +        self.clear()
  1.2329 +        self._initialise()
  1.2330 +        # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
  1.2331 +        #        requires an empty dictionary
  1.2332 +        self.configspec = None
  1.2333 +        # Just to be sure ;-)
  1.2334 +        self._original_configspec = None
  1.2335 +        
  1.2336 +        
  1.2337 +    def reload(self):
  1.2338 +        """
  1.2339 +        Reload a ConfigObj from file.
  1.2340 +        
  1.2341 +        This method raises a ``ReloadError`` if the ConfigObj doesn't have
  1.2342 +        a filename attribute pointing to a file.
  1.2343 +        """
  1.2344 +        if not isinstance(self.filename, basestring):
  1.2345 +            raise ReloadError()
  1.2346 +
  1.2347 +        filename = self.filename
  1.2348 +        current_options = {}
  1.2349 +        for entry in OPTION_DEFAULTS:
  1.2350 +            if entry == 'configspec':
  1.2351 +                continue
  1.2352 +            current_options[entry] = getattr(self, entry)
  1.2353 +            
  1.2354 +        configspec = self._original_configspec
  1.2355 +        current_options['configspec'] = configspec
  1.2356 +            
  1.2357 +        self.clear()
  1.2358 +        self._initialise(current_options)
  1.2359 +        self._load(filename, configspec)
  1.2360 +        
  1.2361 +
  1.2362 +
  1.2363 +class SimpleVal(object):
  1.2364 +    """
  1.2365 +    A simple validator.
  1.2366 +    Can be used to check that all members expected are present.
  1.2367 +    
  1.2368 +    To use it, provide a configspec with all your members in (the value given
  1.2369 +    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
  1.2370 +    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
  1.2371 +    members are present, or a dictionary with True/False meaning
  1.2372 +    present/missing. (Whole missing sections will be replaced with ``False``)
  1.2373 +    """
  1.2374 +    
  1.2375 +    def __init__(self):
  1.2376 +        self.baseErrorClass = ConfigObjError
  1.2377 +    
  1.2378 +    def check(self, check, member, missing=False):
  1.2379 +        """A dummy check method, always returns the value unchanged."""
  1.2380 +        if missing:
  1.2381 +            raise self.baseErrorClass()
  1.2382 +        return member
  1.2383 +
  1.2384 +
  1.2385 +def flatten_errors(cfg, res, levels=None, results=None):
  1.2386 +    """
  1.2387 +    An example function that will turn a nested dictionary of results
  1.2388 +    (as returned by ``ConfigObj.validate``) into a flat list.
  1.2389 +    
  1.2390 +    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
  1.2391 +    dictionary returned by ``validate``.
  1.2392 +    
  1.2393 +    (This is a recursive function, so you shouldn't use the ``levels`` or
  1.2394 +    ``results`` arguments - they are used by the function.)
  1.2395 +    
  1.2396 +    Returns a list of keys that failed. Each member of the list is a tuple::
  1.2397 +    
  1.2398 +        ([list of sections...], key, result)
  1.2399 +    
  1.2400 +    If ``validate`` was called with ``preserve_errors=False`` (the default)
  1.2401 +    then ``result`` will always be ``False``.
  1.2402 +
  1.2403 +    *list of sections* is a flattened list of sections that the key was found
  1.2404 +    in.
  1.2405 +    
  1.2406 +    If the section was missing (or a section was expected and a scalar provided
  1.2407 +    - or vice-versa) then key will be ``None``.
  1.2408 +    
  1.2409 +    If the value (or section) was missing then ``result`` will be ``False``.
  1.2410 +    
  1.2411 +    If ``validate`` was called with ``preserve_errors=True`` and a value
  1.2412 +    was present, but failed the check, then ``result`` will be the exception
  1.2413 +    object returned. You can use this as a string that describes the failure.
  1.2414 +    
  1.2415 +    For example *The value "3" is of the wrong type*.
  1.2416 +    """
  1.2417 +    if levels is None:
  1.2418 +        # first time called
  1.2419 +        levels = []
  1.2420 +        results = []
  1.2421 +    if res == True:
  1.2422 +        return results
  1.2423 +    if res == False or isinstance(res, Exception):
  1.2424 +        results.append((levels[:], None, res))
  1.2425 +        if levels:
  1.2426 +            levels.pop()
  1.2427 +        return results
  1.2428 +    for (key, val) in res.items():
  1.2429 +        if val == True:
  1.2430 +            continue
  1.2431 +        if isinstance(cfg.get(key), dict):
  1.2432 +            # Go down one level
  1.2433 +            levels.append(key)
  1.2434 +            flatten_errors(cfg[key], val, levels, results)
  1.2435 +            continue
  1.2436 +        results.append((levels[:], key, val))
  1.2437 +    #
  1.2438 +    # Go up one level
  1.2439 +    if levels:
  1.2440 +        levels.pop()
  1.2441 +    #
  1.2442 +    return results
  1.2443 +
  1.2444 +
  1.2445 +def get_extra_values(conf, _prepend=()):
  1.2446 +    """
  1.2447 +    Find all the values and sections not in the configspec from a validated
  1.2448 +    ConfigObj.
  1.2449 +    
  1.2450 +    ``get_extra_values`` returns a list of tuples where each tuple represents
  1.2451 +    either an extra section, or an extra value.
  1.2452 +    
  1.2453 +    The tuples contain two values, a tuple representing the section the value 
  1.2454 +    is in and the name of the extra values. For extra values in the top level
  1.2455 +    section the first member will be an empty tuple. For values in the 'foo'
  1.2456 +    section the first member will be ``('foo',)``. For members in the 'bar'
  1.2457 +    subsection of the 'foo' section the first member will be ``('foo', 'bar')``.
  1.2458 +    
  1.2459 +    NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't
  1.2460 +    been validated it will return an empty list.
  1.2461 +    """
  1.2462 +    out = []
  1.2463 +    
  1.2464 +    out.extend([(_prepend, name) for name in conf.extra_values])
  1.2465 +    for name in conf.sections:
  1.2466 +        if name not in conf.extra_values:
  1.2467 +            out.extend(get_extra_values(conf[name], _prepend + (name,)))
  1.2468 +    return out
  1.2469 +
  1.2470 +
  1.2471 +"""*A programming language is a medium of expression.* - Paul Graham"""

mercurial