~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/util/configobj/configobj.py

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 04:10:52 UTC
  • mfrom: (6616.1.1 fix-en-user-guide)
  • Revision ID: pqm@pqm.ubuntu.com-20160421041052-clcye7ns1qcl2n7w
(richard-wilbur) Ensure build of English use guide always uses English text
 even when user's locale specifies a different language. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# configobj.py
 
2
# A config file reader/writer that supports nested sections in config files.
 
3
# Copyright (C) 2005-2009 Michael Foord, Nicola Larosa
 
4
# E-mail: fuzzyman AT voidspace DOT org DOT uk
 
5
#         nico AT tekNico DOT net
 
6
 
 
7
# ConfigObj 4
 
8
# http://www.voidspace.org.uk/python/configobj.html
 
9
 
 
10
# Released subject to the BSD License
 
11
# Please see http://www.voidspace.org.uk/python/license.shtml
 
12
 
 
13
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
 
14
# For information about bugfixes, updates and support, please join the
 
15
# ConfigObj mailing list:
 
16
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
 
17
# Comments, suggestions and bug reports welcome.
 
18
 
 
19
 
 
20
from __future__ import absolute_import
 
21
 
 
22
import sys
 
23
import os
 
24
import re
 
25
 
 
26
compiler = None
 
27
# Bzr modification: Disabled import of 'compiler' module
 
28
# bzr doesn't use the 'unrepr' feature of configobj, so importing compiler just
 
29
# wastes several milliseconds on every single bzr invocation.
 
30
#   -- Andrew Bennetts, 2008-10-14
 
31
#try:
 
32
#    import compiler
 
33
#except ImportError:
 
34
#    # for IronPython
 
35
#    pass
 
36
 
 
37
 
 
38
try:
 
39
    from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
 
40
except ImportError:
 
41
    # Python 2.2 does not have these
 
42
    # UTF-8
 
43
    BOM_UTF8 = '\xef\xbb\xbf'
 
44
    # UTF-16, little endian
 
45
    BOM_UTF16_LE = '\xff\xfe'
 
46
    # UTF-16, big endian
 
47
    BOM_UTF16_BE = '\xfe\xff'
 
48
    if sys.byteorder == 'little':
 
49
        # UTF-16, native endianness
 
50
        BOM_UTF16 = BOM_UTF16_LE
 
51
    else:
 
52
        # UTF-16, native endianness
 
53
        BOM_UTF16 = BOM_UTF16_BE
 
54
 
 
55
# A dictionary mapping BOM to
 
56
# the encoding to decode with, and what to set the
 
57
# encoding attribute to.
 
58
BOMS = {
 
59
    BOM_UTF8: ('utf_8', None),
 
60
    BOM_UTF16_BE: ('utf16_be', 'utf_16'),
 
61
    BOM_UTF16_LE: ('utf16_le', 'utf_16'),
 
62
    BOM_UTF16: ('utf_16', 'utf_16'),
 
63
    }
 
64
# All legal variants of the BOM codecs.
 
65
# TODO: the list of aliases is not meant to be exhaustive, is there a
 
66
#   better way ?
 
67
BOM_LIST = {
 
68
    'utf_16': 'utf_16',
 
69
    'u16': 'utf_16',
 
70
    'utf16': 'utf_16',
 
71
    'utf-16': 'utf_16',
 
72
    'utf16_be': 'utf16_be',
 
73
    'utf_16_be': 'utf16_be',
 
74
    'utf-16be': 'utf16_be',
 
75
    'utf16_le': 'utf16_le',
 
76
    'utf_16_le': 'utf16_le',
 
77
    'utf-16le': 'utf16_le',
 
78
    'utf_8': 'utf_8',
 
79
    'u8': 'utf_8',
 
80
    'utf': 'utf_8',
 
81
    'utf8': 'utf_8',
 
82
    'utf-8': 'utf_8',
 
83
    }
 
84
 
 
85
# Map of encodings to the BOM to write.
 
86
BOM_SET = {
 
87
    'utf_8': BOM_UTF8,
 
88
    'utf_16': BOM_UTF16,
 
89
    'utf16_be': BOM_UTF16_BE,
 
90
    'utf16_le': BOM_UTF16_LE,
 
91
    None: BOM_UTF8
 
92
    }
 
93
 
 
94
 
 
95
def match_utf8(encoding):
 
96
    return BOM_LIST.get(encoding.lower()) == 'utf_8'
 
97
 
 
98
 
 
99
# Quote strings used for writing values
 
100
squot = "'%s'"
 
101
dquot = '"%s"'
 
102
noquot = "%s"
 
103
wspace_plus = ' \r\n\v\t\'"'
 
104
tsquot = '"""%s"""'
 
105
tdquot = "'''%s'''"
 
106
 
 
107
try:
 
108
    enumerate
 
109
except NameError:
 
110
    def enumerate(obj):
 
111
        """enumerate for Python 2.2."""
 
112
        i = -1
 
113
        for item in obj:
 
114
            i += 1
 
115
            yield i, item
 
116
 
 
117
# Sentinel for use in getattr calls to replace hasattr
 
118
MISSING = object()
 
119
 
 
120
__version__ = '4.6.0'
 
121
 
 
122
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
 
123
 
 
124
__docformat__ = "restructuredtext en"
 
125
 
 
126
__all__ = (
 
127
    '__version__',
 
128
    'DEFAULT_INDENT_TYPE',
 
129
    'DEFAULT_INTERPOLATION',
 
130
    'ConfigObjError',
 
131
    'NestingError',
 
132
    'ParseError',
 
133
    'DuplicateError',
 
134
    'ConfigspecError',
 
135
    'ConfigObj',
 
136
    'SimpleVal',
 
137
    'InterpolationError',
 
138
    'InterpolationLoopError',
 
139
    'MissingInterpolationOption',
 
140
    'RepeatSectionError',
 
141
    'ReloadError',
 
142
    'UnreprError',
 
143
    'UnknownType',
 
144
    '__docformat__',
 
145
    'flatten_errors',
 
146
)
 
147
 
 
148
DEFAULT_INTERPOLATION = 'configparser'
 
149
DEFAULT_INDENT_TYPE = '    '
 
150
MAX_INTERPOL_DEPTH = 10
 
151
 
 
152
OPTION_DEFAULTS = {
 
153
    'interpolation': True,
 
154
    'raise_errors': False,
 
155
    'list_values': True,
 
156
    'create_empty': False,
 
157
    'file_error': False,
 
158
    'configspec': None,
 
159
    'stringify': True,
 
160
    # option may be set to one of ('', ' ', '\t')
 
161
    'indent_type': None,
 
162
    'encoding': None,
 
163
    'default_encoding': None,
 
164
    'unrepr': False,
 
165
    'write_empty_values': False,
 
166
}
 
167
 
 
168
 
 
169
 
 
170
def getObj(s):
 
171
    s = "a=" + s
 
172
    if compiler is None:
 
173
        raise ImportError('compiler module not available')
 
174
    p = compiler.parse(s)
 
175
    return p.getChildren()[1].getChildren()[0].getChildren()[1]
 
176
 
 
177
 
 
178
class UnknownType(Exception):
 
179
    pass
 
180
 
 
181
 
 
182
class Builder(object):
 
183
 
 
184
    def build(self, o):
 
185
        m = getattr(self, 'build_' + o.__class__.__name__, None)
 
186
        if m is None:
 
187
            raise UnknownType(o.__class__.__name__)
 
188
        return m(o)
 
189
 
 
190
    def build_List(self, o):
 
191
        return map(self.build, o.getChildren())
 
192
 
 
193
    def build_Const(self, o):
 
194
        return o.value
 
195
 
 
196
    def build_Dict(self, o):
 
197
        d = {}
 
198
        i = iter(map(self.build, o.getChildren()))
 
199
        for el in i:
 
200
            d[el] = i.next()
 
201
        return d
 
202
 
 
203
    def build_Tuple(self, o):
 
204
        return tuple(self.build_List(o))
 
205
 
 
206
    def build_Name(self, o):
 
207
        if o.name == 'None':
 
208
            return None
 
209
        if o.name == 'True':
 
210
            return True
 
211
        if o.name == 'False':
 
212
            return False
 
213
 
 
214
        # An undefined Name
 
215
        raise UnknownType('Undefined Name')
 
216
 
 
217
    def build_Add(self, o):
 
218
        real, imag = map(self.build_Const, o.getChildren())
 
219
        try:
 
220
            real = float(real)
 
221
        except TypeError:
 
222
            raise UnknownType('Add')
 
223
        if not isinstance(imag, complex) or imag.real != 0.0:
 
224
            raise UnknownType('Add')
 
225
        return real+imag
 
226
 
 
227
    def build_Getattr(self, o):
 
228
        parent = self.build(o.expr)
 
229
        return getattr(parent, o.attrname)
 
230
 
 
231
    def build_UnarySub(self, o):
 
232
        return -self.build_Const(o.getChildren()[0])
 
233
 
 
234
    def build_UnaryAdd(self, o):
 
235
        return self.build_Const(o.getChildren()[0])
 
236
 
 
237
 
 
238
_builder = Builder()
 
239
 
 
240
 
 
241
def unrepr(s):
 
242
    if not s:
 
243
        return s
 
244
    return _builder.build(getObj(s))
 
245
 
 
246
 
 
247
 
 
248
class ConfigObjError(SyntaxError):
 
249
    """
 
250
    This is the base class for all errors that ConfigObj raises.
 
251
    It is a subclass of SyntaxError.
 
252
    """
 
253
    def __init__(self, message='', line_number=None, line=''):
 
254
        self.line = line
 
255
        self.line_number = line_number
 
256
        SyntaxError.__init__(self, message)
 
257
 
 
258
 
 
259
class NestingError(ConfigObjError):
 
260
    """
 
261
    This error indicates a level of nesting that doesn't match.
 
262
    """
 
263
 
 
264
 
 
265
class ParseError(ConfigObjError):
 
266
    """
 
267
    This error indicates that a line is badly written.
 
268
    It is neither a valid ``key = value`` line,
 
269
    nor a valid section marker line.
 
270
    """
 
271
 
 
272
 
 
273
class ReloadError(IOError):
 
274
    """
 
275
    A 'reload' operation failed.
 
276
    This exception is a subclass of ``IOError``.
 
277
    """
 
278
    def __init__(self):
 
279
        IOError.__init__(self, 'reload failed, filename is not set.')
 
280
 
 
281
 
 
282
class DuplicateError(ConfigObjError):
 
283
    """
 
284
    The keyword or section specified already exists.
 
285
    """
 
286
 
 
287
 
 
288
class ConfigspecError(ConfigObjError):
 
289
    """
 
290
    An error occured whilst parsing a configspec.
 
291
    """
 
292
 
 
293
 
 
294
class InterpolationError(ConfigObjError):
 
295
    """Base class for the two interpolation errors."""
 
296
 
 
297
 
 
298
class InterpolationLoopError(InterpolationError):
 
299
    """Maximum interpolation depth exceeded in string interpolation."""
 
300
 
 
301
    def __init__(self, option):
 
302
        InterpolationError.__init__(
 
303
            self,
 
304
            'interpolation loop detected in value "%s".' % option)
 
305
 
 
306
 
 
307
class RepeatSectionError(ConfigObjError):
 
308
    """
 
309
    This error indicates additional sections in a section with a
 
310
    ``__many__`` (repeated) section.
 
311
    """
 
312
 
 
313
 
 
314
class MissingInterpolationOption(InterpolationError):
 
315
    """A value specified for interpolation was missing."""
 
316
 
 
317
    def __init__(self, option):
 
318
        InterpolationError.__init__(
 
319
            self,
 
320
            'missing option "%s" in interpolation.' % option)
 
321
 
 
322
 
 
323
class UnreprError(ConfigObjError):
 
324
    """An error parsing in unrepr mode."""
 
325
 
 
326
 
 
327
 
 
328
class InterpolationEngine(object):
 
329
    """
 
330
    A helper class to help perform string interpolation.
 
331
 
 
332
    This class is an abstract base class; its descendants perform
 
333
    the actual work.
 
334
    """
 
335
 
 
336
    # compiled regexp to use in self.interpolate()
 
337
    _KEYCRE = re.compile(r"%\(([^)]*)\)s")
 
338
 
 
339
    def __init__(self, section):
 
340
        # the Section instance that "owns" this engine
 
341
        self.section = section
 
342
 
 
343
 
 
344
    def interpolate(self, key, value):
 
345
        def recursive_interpolate(key, value, section, backtrail):
 
346
            """The function that does the actual work.
 
347
 
 
348
            ``value``: the string we're trying to interpolate.
 
349
            ``section``: the section in which that string was found
 
350
            ``backtrail``: a dict to keep track of where we've been,
 
351
            to detect and prevent infinite recursion loops
 
352
 
 
353
            This is similar to a depth-first-search algorithm.
 
354
            """
 
355
            # Have we been here already?
 
356
            if (key, section.name) in backtrail:
 
357
                # Yes - infinite loop detected
 
358
                raise InterpolationLoopError(key)
 
359
            # Place a marker on our backtrail so we won't come back here again
 
360
            backtrail[(key, section.name)] = 1
 
361
 
 
362
            # Now start the actual work
 
363
            match = self._KEYCRE.search(value)
 
364
            while match:
 
365
                # The actual parsing of the match is implementation-dependent,
 
366
                # so delegate to our helper function
 
367
                k, v, s = self._parse_match(match)
 
368
                if k is None:
 
369
                    # That's the signal that no further interpolation is needed
 
370
                    replacement = v
 
371
                else:
 
372
                    # Further interpolation may be needed to obtain final value
 
373
                    replacement = recursive_interpolate(k, v, s, backtrail)
 
374
                # Replace the matched string with its final value
 
375
                start, end = match.span()
 
376
                value = ''.join((value[:start], replacement, value[end:]))
 
377
                new_search_start = start + len(replacement)
 
378
                # Pick up the next interpolation key, if any, for next time
 
379
                # through the while loop
 
380
                match = self._KEYCRE.search(value, new_search_start)
 
381
 
 
382
            # Now safe to come back here again; remove marker from backtrail
 
383
            del backtrail[(key, section.name)]
 
384
 
 
385
            return value
 
386
 
 
387
        # Back in interpolate(), all we have to do is kick off the recursive
 
388
        # function with appropriate starting values
 
389
        value = recursive_interpolate(key, value, self.section, {})
 
390
        return value
 
391
 
 
392
 
 
393
    def _fetch(self, key):
 
394
        """Helper function to fetch values from owning section.
 
395
 
 
396
        Returns a 2-tuple: the value, and the section where it was found.
 
397
        """
 
398
        # switch off interpolation before we try and fetch anything !
 
399
        save_interp = self.section.main.interpolation
 
400
        self.section.main.interpolation = False
 
401
 
 
402
        # Start at section that "owns" this InterpolationEngine
 
403
        current_section = self.section
 
404
        while True:
 
405
            # try the current section first
 
406
            val = current_section.get(key)
 
407
            if val is not None:
 
408
                break
 
409
            # try "DEFAULT" next
 
410
            val = current_section.get('DEFAULT', {}).get(key)
 
411
            if val is not None:
 
412
                break
 
413
            # move up to parent and try again
 
414
            # top-level's parent is itself
 
415
            if current_section.parent is current_section:
 
416
                # reached top level, time to give up
 
417
                break
 
418
            current_section = current_section.parent
 
419
 
 
420
        # restore interpolation to previous value before returning
 
421
        self.section.main.interpolation = save_interp
 
422
        if val is None:
 
423
            raise MissingInterpolationOption(key)
 
424
        return val, current_section
 
425
 
 
426
 
 
427
    def _parse_match(self, match):
 
428
        """Implementation-dependent helper function.
 
429
 
 
430
        Will be passed a match object corresponding to the interpolation
 
431
        key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
 
432
        key in the appropriate config file section (using the ``_fetch()``
 
433
        helper function) and return a 3-tuple: (key, value, section)
 
434
 
 
435
        ``key`` is the name of the key we're looking for
 
436
        ``value`` is the value found for that key
 
437
        ``section`` is a reference to the section where it was found
 
438
 
 
439
        ``key`` and ``section`` should be None if no further
 
440
        interpolation should be performed on the resulting value
 
441
        (e.g., if we interpolated "$$" and returned "$").
 
442
        """
 
443
        raise NotImplementedError()
 
444
 
 
445
 
 
446
 
 
447
class ConfigParserInterpolation(InterpolationEngine):
 
448
    """Behaves like ConfigParser."""
 
449
    _KEYCRE = re.compile(r"%\(([^)]*)\)s")
 
450
 
 
451
    def _parse_match(self, match):
 
452
        key = match.group(1)
 
453
        value, section = self._fetch(key)
 
454
        return key, value, section
 
455
 
 
456
 
 
457
 
 
458
class TemplateInterpolation(InterpolationEngine):
 
459
    """Behaves like string.Template."""
 
460
    _delimiter = '$'
 
461
    _KEYCRE = re.compile(r"""
 
462
        \$(?:
 
463
          (?P<escaped>\$)              |   # Two $ signs
 
464
          (?P<named>[_a-z][_a-z0-9]*)  |   # $name format
 
465
          {(?P<braced>[^}]*)}              # ${name} format
 
466
        )
 
467
        """, re.IGNORECASE | re.VERBOSE)
 
468
 
 
469
    def _parse_match(self, match):
 
470
        # Valid name (in or out of braces): fetch value from section
 
471
        key = match.group('named') or match.group('braced')
 
472
        if key is not None:
 
473
            value, section = self._fetch(key)
 
474
            return key, value, section
 
475
        # Escaped delimiter (e.g., $$): return single delimiter
 
476
        if match.group('escaped') is not None:
 
477
            # Return None for key and section to indicate it's time to stop
 
478
            return None, self._delimiter, None
 
479
        # Anything else: ignore completely, just return it unchanged
 
480
        return None, match.group(), None
 
481
 
 
482
 
 
483
interpolation_engines = {
 
484
    'configparser': ConfigParserInterpolation,
 
485
    'template': TemplateInterpolation,
 
486
}
 
487
 
 
488
 
 
489
def __newobj__(cls, *args):
 
490
    # Hack for pickle
 
491
    return cls.__new__(cls, *args)
 
492
 
 
493
class Section(dict):
 
494
    """
 
495
    A dictionary-like object that represents a section in a config file.
 
496
 
 
497
    It does string interpolation if the 'interpolation' attribute
 
498
    of the 'main' object is set to True.
 
499
 
 
500
    Interpolation is tried first from this object, then from the 'DEFAULT'
 
501
    section of this object, next from the parent and its 'DEFAULT' section,
 
502
    and so on until the main object is reached.
 
503
 
 
504
    A Section will behave like an ordered dictionary - following the
 
505
    order of the ``scalars`` and ``sections`` attributes.
 
506
    You can use this to change the order of members.
 
507
 
 
508
    Iteration follows the order: scalars, then sections.
 
509
    """
 
510
 
 
511
 
 
512
    def __setstate__(self, state):
 
513
        dict.update(self, state[0])
 
514
        self.__dict__.update(state[1])
 
515
 
 
516
    def __reduce__(self):
 
517
        state = (dict(self), self.__dict__)
 
518
        return (__newobj__, (self.__class__,), state)
 
519
 
 
520
 
 
521
    def __init__(self, parent, depth, main, indict=None, name=None):
 
522
        """
 
523
        * parent is the section above
 
524
        * depth is the depth level of this section
 
525
        * main is the main ConfigObj
 
526
        * indict is a dictionary to initialise the section with
 
527
        """
 
528
        if indict is None:
 
529
            indict = {}
 
530
        dict.__init__(self)
 
531
        # used for nesting level *and* interpolation
 
532
        self.parent = parent
 
533
        # used for the interpolation attribute
 
534
        self.main = main
 
535
        # level of nesting depth of this Section
 
536
        self.depth = depth
 
537
        # purely for information
 
538
        self.name = name
 
539
        #
 
540
        self._initialise()
 
541
        # we do this explicitly so that __setitem__ is used properly
 
542
        # (rather than just passing to ``dict.__init__``)
 
543
        for entry, value in indict.iteritems():
 
544
            self[entry] = value
 
545
 
 
546
 
 
547
    def _initialise(self):
 
548
        # the sequence of scalar values in this Section
 
549
        self.scalars = []
 
550
        # the sequence of sections in this Section
 
551
        self.sections = []
 
552
        # for comments :-)
 
553
        self.comments = {}
 
554
        self.inline_comments = {}
 
555
        # the configspec
 
556
        self.configspec = None
 
557
        # for defaults
 
558
        self.defaults = []
 
559
        self.default_values = {}
 
560
 
 
561
 
 
562
    def _interpolate(self, key, value):
 
563
        try:
 
564
            # do we already have an interpolation engine?
 
565
            engine = self._interpolation_engine
 
566
        except AttributeError:
 
567
            # not yet: first time running _interpolate(), so pick the engine
 
568
            name = self.main.interpolation
 
569
            if name == True:  # note that "if name:" would be incorrect here
 
570
                # backwards-compatibility: interpolation=True means use default
 
571
                name = DEFAULT_INTERPOLATION
 
572
            name = name.lower()  # so that "Template", "template", etc. all work
 
573
            class_ = interpolation_engines.get(name, None)
 
574
            if class_ is None:
 
575
                # invalid value for self.main.interpolation
 
576
                self.main.interpolation = False
 
577
                return value
 
578
            else:
 
579
                # save reference to engine so we don't have to do this again
 
580
                engine = self._interpolation_engine = class_(self)
 
581
        # let the engine do the actual work
 
582
        return engine.interpolate(key, value)
 
583
 
 
584
 
 
585
    def __getitem__(self, key):
 
586
        """Fetch the item and do string interpolation."""
 
587
        val = dict.__getitem__(self, key)
 
588
        if self.main.interpolation and isinstance(val, basestring):
 
589
            return self._interpolate(key, val)
 
590
        return val
 
591
 
 
592
 
 
593
    def __setitem__(self, key, value, unrepr=False):
 
594
        """
 
595
        Correctly set a value.
 
596
 
 
597
        Making dictionary values Section instances.
 
598
        (We have to special case 'Section' instances - which are also dicts)
 
599
 
 
600
        Keys must be strings.
 
601
        Values need only be strings (or lists of strings) if
 
602
        ``main.stringify`` is set.
 
603
 
 
604
        ``unrepr`` must be set when setting a value to a dictionary, without
 
605
        creating a new sub-section.
 
606
        """
 
607
        if not isinstance(key, basestring):
 
608
            raise ValueError('The key "%s" is not a string.' % key)
 
609
 
 
610
        # add the comment
 
611
        if key not in self.comments:
 
612
            self.comments[key] = []
 
613
            self.inline_comments[key] = ''
 
614
        # remove the entry from defaults
 
615
        if key in self.defaults:
 
616
            self.defaults.remove(key)
 
617
        #
 
618
        if isinstance(value, Section):
 
619
            if key not in self:
 
620
                self.sections.append(key)
 
621
            dict.__setitem__(self, key, value)
 
622
        elif isinstance(value, dict) and not unrepr:
 
623
            # First create the new depth level,
 
624
            # then create the section
 
625
            if key not in self:
 
626
                self.sections.append(key)
 
627
            new_depth = self.depth + 1
 
628
            dict.__setitem__(
 
629
                self,
 
630
                key,
 
631
                Section(
 
632
                    self,
 
633
                    new_depth,
 
634
                    self.main,
 
635
                    indict=value,
 
636
                    name=key))
 
637
        else:
 
638
            if key not in self:
 
639
                self.scalars.append(key)
 
640
            if not self.main.stringify:
 
641
                if isinstance(value, basestring):
 
642
                    pass
 
643
                elif isinstance(value, (list, tuple)):
 
644
                    for entry in value:
 
645
                        if not isinstance(entry, basestring):
 
646
                            raise TypeError('Value is not a string "%s".' % entry)
 
647
                else:
 
648
                    raise TypeError('Value is not a string "%s".' % value)
 
649
            dict.__setitem__(self, key, value)
 
650
 
 
651
 
 
652
    def __delitem__(self, key):
 
653
        """Remove items from the sequence when deleting."""
 
654
        dict. __delitem__(self, key)
 
655
        if key in self.scalars:
 
656
            self.scalars.remove(key)
 
657
        else:
 
658
            self.sections.remove(key)
 
659
        del self.comments[key]
 
660
        del self.inline_comments[key]
 
661
 
 
662
 
 
663
    def get(self, key, default=None):
 
664
        """A version of ``get`` that doesn't bypass string interpolation."""
 
665
        try:
 
666
            return self[key]
 
667
        except KeyError:
 
668
            return default
 
669
 
 
670
 
 
671
    def update(self, indict):
 
672
        """
 
673
        A version of update that uses our ``__setitem__``.
 
674
        """
 
675
        for entry in indict:
 
676
            self[entry] = indict[entry]
 
677
 
 
678
 
 
679
    def pop(self, key, *args):
 
680
        """
 
681
        'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 
682
        If key is not found, d is returned if given, otherwise KeyError is raised'
 
683
        """
 
684
        val = dict.pop(self, key, *args)
 
685
        if key in self.scalars:
 
686
            del self.comments[key]
 
687
            del self.inline_comments[key]
 
688
            self.scalars.remove(key)
 
689
        elif key in self.sections:
 
690
            del self.comments[key]
 
691
            del self.inline_comments[key]
 
692
            self.sections.remove(key)
 
693
        if self.main.interpolation and isinstance(val, basestring):
 
694
            return self._interpolate(key, val)
 
695
        return val
 
696
 
 
697
 
 
698
    def popitem(self):
 
699
        """Pops the first (key,val)"""
 
700
        sequence = (self.scalars + self.sections)
 
701
        if not sequence:
 
702
            raise KeyError(": 'popitem(): dictionary is empty'")
 
703
        key = sequence[0]
 
704
        val =  self[key]
 
705
        del self[key]
 
706
        return key, val
 
707
 
 
708
 
 
709
    def clear(self):
 
710
        """
 
711
        A version of clear that also affects scalars/sections
 
712
        Also clears comments and configspec.
 
713
 
 
714
        Leaves other attributes alone :
 
715
            depth/main/parent are not affected
 
716
        """
 
717
        dict.clear(self)
 
718
        self.scalars = []
 
719
        self.sections = []
 
720
        self.comments = {}
 
721
        self.inline_comments = {}
 
722
        self.configspec = None
 
723
 
 
724
 
 
725
    def setdefault(self, key, default=None):
 
726
        """A version of setdefault that sets sequence if appropriate."""
 
727
        try:
 
728
            return self[key]
 
729
        except KeyError:
 
730
            self[key] = default
 
731
            return self[key]
 
732
 
 
733
 
 
734
    def items(self):
 
735
        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
 
736
        return zip((self.scalars + self.sections), self.values())
 
737
 
 
738
 
 
739
    def keys(self):
 
740
        """D.keys() -> list of D's keys"""
 
741
        return (self.scalars + self.sections)
 
742
 
 
743
 
 
744
    def values(self):
 
745
        """D.values() -> list of D's values"""
 
746
        return [self[key] for key in (self.scalars + self.sections)]
 
747
 
 
748
 
 
749
    def iteritems(self):
 
750
        """D.iteritems() -> an iterator over the (key, value) items of D"""
 
751
        return iter(self.items())
 
752
 
 
753
 
 
754
    def iterkeys(self):
 
755
        """D.iterkeys() -> an iterator over the keys of D"""
 
756
        return iter((self.scalars + self.sections))
 
757
 
 
758
    __iter__ = iterkeys
 
759
 
 
760
 
 
761
    def itervalues(self):
 
762
        """D.itervalues() -> an iterator over the values of D"""
 
763
        return iter(self.values())
 
764
 
 
765
 
 
766
    def __repr__(self):
 
767
        """x.__repr__() <==> repr(x)"""
 
768
        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
 
769
            for key in (self.scalars + self.sections)])
 
770
 
 
771
    __str__ = __repr__
 
772
    __str__.__doc__ = "x.__str__() <==> str(x)"
 
773
 
 
774
 
 
775
    # Extra methods - not in a normal dictionary
 
776
 
 
777
    def dict(self):
 
778
        """
 
779
        Return a deepcopy of self as a dictionary.
 
780
 
 
781
        All members that are ``Section`` instances are recursively turned to
 
782
        ordinary dictionaries - by calling their ``dict`` method.
 
783
 
 
784
        >>> n = a.dict()
 
785
        >>> n == a
 
786
        1
 
787
        >>> n is a
 
788
        0
 
789
        """
 
790
        newdict = {}
 
791
        for entry in self:
 
792
            this_entry = self[entry]
 
793
            if isinstance(this_entry, Section):
 
794
                this_entry = this_entry.dict()
 
795
            elif isinstance(this_entry, list):
 
796
                # create a copy rather than a reference
 
797
                this_entry = list(this_entry)
 
798
            elif isinstance(this_entry, tuple):
 
799
                # create a copy rather than a reference
 
800
                this_entry = tuple(this_entry)
 
801
            newdict[entry] = this_entry
 
802
        return newdict
 
803
 
 
804
 
 
805
    def merge(self, indict):
 
806
        """
 
807
        A recursive update - useful for merging config files.
 
808
 
 
809
        >>> a = '''[section1]
 
810
        ...     option1 = True
 
811
        ...     [[subsection]]
 
812
        ...     more_options = False
 
813
        ...     # end of file'''.splitlines()
 
814
        >>> b = '''# File is user.ini
 
815
        ...     [section1]
 
816
        ...     option1 = False
 
817
        ...     # end of file'''.splitlines()
 
818
        >>> c1 = ConfigObj(b)
 
819
        >>> c2 = ConfigObj(a)
 
820
        >>> c2.merge(c1)
 
821
        >>> c2
 
822
        ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
 
823
        """
 
824
        for key, val in indict.items():
 
825
            if (key in self and isinstance(self[key], dict) and
 
826
                                isinstance(val, dict)):
 
827
                self[key].merge(val)
 
828
            else:
 
829
                self[key] = val
 
830
 
 
831
 
 
832
    def rename(self, oldkey, newkey):
 
833
        """
 
834
        Change a keyname to another, without changing position in sequence.
 
835
 
 
836
        Implemented so that transformations can be made on keys,
 
837
        as well as on values. (used by encode and decode)
 
838
 
 
839
        Also renames comments.
 
840
        """
 
841
        if oldkey in self.scalars:
 
842
            the_list = self.scalars
 
843
        elif oldkey in self.sections:
 
844
            the_list = self.sections
 
845
        else:
 
846
            raise KeyError('Key "%s" not found.' % oldkey)
 
847
        pos = the_list.index(oldkey)
 
848
        #
 
849
        val = self[oldkey]
 
850
        dict.__delitem__(self, oldkey)
 
851
        dict.__setitem__(self, newkey, val)
 
852
        the_list.remove(oldkey)
 
853
        the_list.insert(pos, newkey)
 
854
        comm = self.comments[oldkey]
 
855
        inline_comment = self.inline_comments[oldkey]
 
856
        del self.comments[oldkey]
 
857
        del self.inline_comments[oldkey]
 
858
        self.comments[newkey] = comm
 
859
        self.inline_comments[newkey] = inline_comment
 
860
 
 
861
 
 
862
    def walk(self, function, raise_errors=True,
 
863
            call_on_sections=False, **keywargs):
 
864
        """
 
865
        Walk every member and call a function on the keyword and value.
 
866
 
 
867
        Return a dictionary of the return values
 
868
 
 
869
        If the function raises an exception, raise the errror
 
870
        unless ``raise_errors=False``, in which case set the return value to
 
871
        ``False``.
 
872
 
 
873
        Any unrecognised keyword arguments you pass to walk, will be pased on
 
874
        to the function you pass in.
 
875
 
 
876
        Note: if ``call_on_sections`` is ``True`` then - on encountering a
 
877
        subsection, *first* the function is called for the *whole* subsection,
 
878
        and then recurses into it's members. This means your function must be
 
879
        able to handle strings, dictionaries and lists. This allows you
 
880
        to change the key of subsections as well as for ordinary members. The
 
881
        return value when called on the whole subsection has to be discarded.
 
882
 
 
883
        See  the encode and decode methods for examples, including functions.
 
884
 
 
885
        .. admonition:: caution
 
886
 
 
887
            You can use ``walk`` to transform the names of members of a section
 
888
            but you mustn't add or delete members.
 
889
 
 
890
        >>> config = '''[XXXXsection]
 
891
        ... XXXXkey = XXXXvalue'''.splitlines()
 
892
        >>> cfg = ConfigObj(config)
 
893
        >>> cfg
 
894
        ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
 
895
        >>> def transform(section, key):
 
896
        ...     val = section[key]
 
897
        ...     newkey = key.replace('XXXX', 'CLIENT1')
 
898
        ...     section.rename(key, newkey)
 
899
        ...     if isinstance(val, (tuple, list, dict)):
 
900
        ...         pass
 
901
        ...     else:
 
902
        ...         val = val.replace('XXXX', 'CLIENT1')
 
903
        ...         section[newkey] = val
 
904
        >>> cfg.walk(transform, call_on_sections=True)
 
905
        {'CLIENT1section': {'CLIENT1key': None}}
 
906
        >>> cfg
 
907
        ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
 
908
        """
 
909
        out = {}
 
910
        # scalars first
 
911
        for i in range(len(self.scalars)):
 
912
            entry = self.scalars[i]
 
913
            try:
 
914
                val = function(self, entry, **keywargs)
 
915
                # bound again in case name has changed
 
916
                entry = self.scalars[i]
 
917
                out[entry] = val
 
918
            except Exception:
 
919
                if raise_errors:
 
920
                    raise
 
921
                else:
 
922
                    entry = self.scalars[i]
 
923
                    out[entry] = False
 
924
        # then sections
 
925
        for i in range(len(self.sections)):
 
926
            entry = self.sections[i]
 
927
            if call_on_sections:
 
928
                try:
 
929
                    function(self, entry, **keywargs)
 
930
                except Exception:
 
931
                    if raise_errors:
 
932
                        raise
 
933
                    else:
 
934
                        entry = self.sections[i]
 
935
                        out[entry] = False
 
936
                # bound again in case name has changed
 
937
                entry = self.sections[i]
 
938
            # previous result is discarded
 
939
            out[entry] = self[entry].walk(
 
940
                function,
 
941
                raise_errors=raise_errors,
 
942
                call_on_sections=call_on_sections,
 
943
                **keywargs)
 
944
        return out
 
945
 
 
946
 
 
947
    def as_bool(self, key):
 
948
        """
 
949
        Accepts a key as input. The corresponding value must be a string or
 
950
        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
 
951
        retain compatibility with Python 2.2.
 
952
 
 
953
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns
 
954
        ``True``.
 
955
 
 
956
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns
 
957
        ``False``.
 
958
 
 
959
        ``as_bool`` is not case sensitive.
 
960
 
 
961
        Any other input will raise a ``ValueError``.
 
962
 
 
963
        >>> a = ConfigObj()
 
964
        >>> a['a'] = 'fish'
 
965
        >>> a.as_bool('a')
 
966
        Traceback (most recent call last):
 
967
        ValueError: Value "fish" is neither True nor False
 
968
        >>> a['b'] = 'True'
 
969
        >>> a.as_bool('b')
 
970
        1
 
971
        >>> a['b'] = 'off'
 
972
        >>> a.as_bool('b')
 
973
        0
 
974
        """
 
975
        val = self[key]
 
976
        if val == True:
 
977
            return True
 
978
        elif val == False:
 
979
            return False
 
980
        else:
 
981
            try:
 
982
                if not isinstance(val, basestring):
 
983
                    # TODO: Why do we raise a KeyError here?
 
984
                    raise KeyError()
 
985
                else:
 
986
                    return self.main._bools[val.lower()]
 
987
            except KeyError:
 
988
                raise ValueError('Value "%s" is neither True nor False' % val)
 
989
 
 
990
 
 
991
    def as_int(self, key):
 
992
        """
 
993
        A convenience method which coerces the specified value to an integer.
 
994
 
 
995
        If the value is an invalid literal for ``int``, a ``ValueError`` will
 
996
        be raised.
 
997
 
 
998
        >>> a = ConfigObj()
 
999
        >>> a['a'] = 'fish'
 
1000
        >>> a.as_int('a')
 
1001
        Traceback (most recent call last):
 
1002
        ValueError: invalid literal for int() with base 10: 'fish'
 
1003
        >>> a['b'] = '1'
 
1004
        >>> a.as_int('b')
 
1005
        1
 
1006
        >>> a['b'] = '3.2'
 
1007
        >>> a.as_int('b')
 
1008
        Traceback (most recent call last):
 
1009
        ValueError: invalid literal for int() with base 10: '3.2'
 
1010
        """
 
1011
        return int(self[key])
 
1012
 
 
1013
 
 
1014
    def as_float(self, key):
 
1015
        """
 
1016
        A convenience method which coerces the specified value to a float.
 
1017
 
 
1018
        If the value is an invalid literal for ``float``, a ``ValueError`` will
 
1019
        be raised.
 
1020
 
 
1021
        >>> a = ConfigObj()
 
1022
        >>> a['a'] = 'fish'
 
1023
        >>> a.as_float('a')
 
1024
        Traceback (most recent call last):
 
1025
        ValueError: invalid literal for float(): fish
 
1026
        >>> a['b'] = '1'
 
1027
        >>> a.as_float('b')
 
1028
        1.0
 
1029
        >>> a['b'] = '3.2'
 
1030
        >>> a.as_float('b')
 
1031
        3.2000000000000002
 
1032
        """
 
1033
        return float(self[key])
 
1034
 
 
1035
 
 
1036
    def as_list(self, key):
 
1037
        """
 
1038
        A convenience method which fetches the specified value, guaranteeing
 
1039
        that it is a list.
 
1040
 
 
1041
        >>> a = ConfigObj()
 
1042
        >>> a['a'] = 1
 
1043
        >>> a.as_list('a')
 
1044
        [1]
 
1045
        >>> a['a'] = (1,)
 
1046
        >>> a.as_list('a')
 
1047
        [1]
 
1048
        >>> a['a'] = [1]
 
1049
        >>> a.as_list('a')
 
1050
        [1]
 
1051
        """
 
1052
        result = self[key]
 
1053
        if isinstance(result, (tuple, list)):
 
1054
            return list(result)
 
1055
        return [result]
 
1056
 
 
1057
 
 
1058
    def restore_default(self, key):
 
1059
        """
 
1060
        Restore (and return) default value for the specified key.
 
1061
 
 
1062
        This method will only work for a ConfigObj that was created
 
1063
        with a configspec and has been validated.
 
1064
 
 
1065
        If there is no default value for this key, ``KeyError`` is raised.
 
1066
        """
 
1067
        default = self.default_values[key]
 
1068
        dict.__setitem__(self, key, default)
 
1069
        if key not in self.defaults:
 
1070
            self.defaults.append(key)
 
1071
        return default
 
1072
 
 
1073
 
 
1074
    def restore_defaults(self):
 
1075
        """
 
1076
        Recursively restore default values to all members
 
1077
        that have them.
 
1078
 
 
1079
        This method will only work for a ConfigObj that was created
 
1080
        with a configspec and has been validated.
 
1081
 
 
1082
        It doesn't delete or modify entries without default values.
 
1083
        """
 
1084
        for key in self.default_values:
 
1085
            self.restore_default(key)
 
1086
 
 
1087
        for section in self.sections:
 
1088
            self[section].restore_defaults()
 
1089
 
 
1090
 
 
1091
class ConfigObj(Section):
 
1092
    """An object to read, create, and write config files."""
 
1093
 
 
1094
    _keyword = re.compile(r'''^ # line start
 
1095
        (\s*)                   # indentation
 
1096
        (                       # keyword
 
1097
            (?:".*?")|          # double quotes
 
1098
            (?:'.*?')|          # single quotes
 
1099
            (?:[^'"=].*?)       # no quotes
 
1100
        )
 
1101
        \s*=\s*                 # divider
 
1102
        (.*)                    # value (including list values and comments)
 
1103
        $   # line end
 
1104
        ''',
 
1105
        re.VERBOSE)
 
1106
 
 
1107
    _sectionmarker = re.compile(r'''^
 
1108
        (\s*)                     # 1: indentation
 
1109
        ((?:\[\s*)+)              # 2: section marker open
 
1110
        (                         # 3: section name open
 
1111
            (?:"\s*\S.*?\s*")|    # at least one non-space with double quotes
 
1112
            (?:'\s*\S.*?\s*')|    # at least one non-space with single quotes
 
1113
            (?:[^'"\s].*?)        # at least one non-space unquoted
 
1114
        )                         # section name close
 
1115
        ((?:\s*\])+)              # 4: section marker close
 
1116
        \s*(\#.*)?                # 5: optional comment
 
1117
        $''',
 
1118
        re.VERBOSE)
 
1119
 
 
1120
    # this regexp pulls list values out as a single string
 
1121
    # or single values and comments
 
1122
    # FIXME: this regex adds a '' to the end of comma terminated lists
 
1123
    #   workaround in ``_handle_value``
 
1124
    _valueexp = re.compile(r'''^
 
1125
        (?:
 
1126
            (?:
 
1127
                (
 
1128
                    (?:
 
1129
                        (?:
 
1130
                            (?:".*?")|              # double quotes
 
1131
                            (?:'.*?')|              # single quotes
 
1132
                            (?:[^'",\#][^,\#]*?)    # unquoted
 
1133
                        )
 
1134
                        \s*,\s*                     # comma
 
1135
                    )*      # match all list items ending in a comma (if any)
 
1136
                )
 
1137
                (
 
1138
                    (?:".*?")|                      # double quotes
 
1139
                    (?:'.*?')|                      # single quotes
 
1140
                    (?:[^'",\#\s][^,]*?)|           # unquoted
 
1141
                    (?:(?<!,))                      # Empty value
 
1142
                )?          # last item in a list - or string value
 
1143
            )|
 
1144
            (,)             # alternatively a single comma - empty list
 
1145
        )
 
1146
        \s*(\#.*)?          # optional comment
 
1147
        $''',
 
1148
        re.VERBOSE)
 
1149
 
 
1150
    # use findall to get the members of a list value
 
1151
    _listvalueexp = re.compile(r'''
 
1152
        (
 
1153
            (?:".*?")|          # double quotes
 
1154
            (?:'.*?')|          # single quotes
 
1155
            (?:[^'",\#].*?)       # unquoted
 
1156
        )
 
1157
        \s*,\s*                 # comma
 
1158
        ''',
 
1159
        re.VERBOSE)
 
1160
 
 
1161
    # this regexp is used for the value
 
1162
    # when lists are switched off
 
1163
    _nolistvalue = re.compile(r'''^
 
1164
        (
 
1165
            (?:".*?")|          # double quotes
 
1166
            (?:'.*?')|          # single quotes
 
1167
            (?:[^'"\#].*?)|     # unquoted
 
1168
            (?:)                # Empty value
 
1169
        )
 
1170
        \s*(\#.*)?              # optional comment
 
1171
        $''',
 
1172
        re.VERBOSE)
 
1173
 
 
1174
    # regexes for finding triple quoted values on one line
 
1175
    _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
 
1176
    _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
 
1177
    _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
 
1178
    _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
 
1179
 
 
1180
    _triple_quote = {
 
1181
        "'''": (_single_line_single, _multi_line_single),
 
1182
        '"""': (_single_line_double, _multi_line_double),
 
1183
    }
 
1184
 
 
1185
    # Used by the ``istrue`` Section method
 
1186
    _bools = {
 
1187
        'yes': True, 'no': False,
 
1188
        'on': True, 'off': False,
 
1189
        '1': True, '0': False,
 
1190
        'true': True, 'false': False,
 
1191
        }
 
1192
 
 
1193
 
 
1194
    def __init__(self, infile=None, options=None, _inspec=False, **kwargs):
 
1195
        """
 
1196
        Parse a config file or create a config file object.
 
1197
 
 
1198
        ``ConfigObj(infile=None, options=None, **kwargs)``
 
1199
        """
 
1200
        self._inspec = _inspec
 
1201
        # init the superclass
 
1202
        Section.__init__(self, self, 0, self)
 
1203
 
 
1204
        infile = infile or []
 
1205
        options = dict(options or {})
 
1206
 
 
1207
        # keyword arguments take precedence over an options dictionary
 
1208
        options.update(kwargs)
 
1209
        if _inspec:
 
1210
            options['list_values'] = False
 
1211
 
 
1212
        defaults = OPTION_DEFAULTS.copy()
 
1213
        # TODO: check the values too.
 
1214
        for entry in options:
 
1215
            if entry not in defaults:
 
1216
                raise TypeError('Unrecognised option "%s".' % entry)
 
1217
 
 
1218
        # Add any explicit options to the defaults
 
1219
        defaults.update(options)
 
1220
        self._initialise(defaults)
 
1221
        configspec = defaults['configspec']
 
1222
        self._original_configspec = configspec
 
1223
        self._load(infile, configspec)
 
1224
 
 
1225
 
 
1226
    def _load(self, infile, configspec):
 
1227
        if isinstance(infile, basestring):
 
1228
            self.filename = infile
 
1229
            if os.path.isfile(infile):
 
1230
                h = open(infile, 'rb')
 
1231
                infile = h.read() or []
 
1232
                h.close()
 
1233
            elif self.file_error:
 
1234
                # raise an error if the file doesn't exist
 
1235
                raise IOError('Config file not found: "%s".' % self.filename)
 
1236
            else:
 
1237
                # file doesn't already exist
 
1238
                if self.create_empty:
 
1239
                    # this is a good test that the filename specified
 
1240
                    # isn't impossible - like on a non-existent device
 
1241
                    h = open(infile, 'w')
 
1242
                    h.write('')
 
1243
                    h.close()
 
1244
                infile = []
 
1245
 
 
1246
        elif isinstance(infile, (list, tuple)):
 
1247
            infile = list(infile)
 
1248
 
 
1249
        elif isinstance(infile, dict):
 
1250
            # initialise self
 
1251
            # the Section class handles creating subsections
 
1252
            if isinstance(infile, ConfigObj):
 
1253
                # get a copy of our ConfigObj
 
1254
                infile = infile.dict()
 
1255
 
 
1256
            for entry in infile:
 
1257
                self[entry] = infile[entry]
 
1258
            del self._errors
 
1259
 
 
1260
            if configspec is not None:
 
1261
                self._handle_configspec(configspec)
 
1262
            else:
 
1263
                self.configspec = None
 
1264
            return
 
1265
 
 
1266
        elif getattr(infile, 'read', MISSING) is not MISSING:
 
1267
            # This supports file like objects
 
1268
            infile = infile.read() or []
 
1269
            # needs splitting into lines - but needs doing *after* decoding
 
1270
            # in case it's not an 8 bit encoding
 
1271
        else:
 
1272
            raise TypeError('infile must be a filename, file like object, or list of lines.')
 
1273
 
 
1274
        if infile:
 
1275
            # don't do it for the empty ConfigObj
 
1276
            infile = self._handle_bom(infile)
 
1277
            # infile is now *always* a list
 
1278
            #
 
1279
            # Set the newlines attribute (first line ending it finds)
 
1280
            # and strip trailing '\n' or '\r' from lines
 
1281
            for line in infile:
 
1282
                if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
 
1283
                    continue
 
1284
                for end in ('\r\n', '\n', '\r'):
 
1285
                    if line.endswith(end):
 
1286
                        self.newlines = end
 
1287
                        break
 
1288
                break
 
1289
 
 
1290
            infile = [line.rstrip('\r\n') for line in infile]
 
1291
 
 
1292
        self._parse(infile)
 
1293
        # if we had any errors, now is the time to raise them
 
1294
        if self._errors:
 
1295
            info = "at line %s." % self._errors[0].line_number
 
1296
            if len(self._errors) > 1:
 
1297
                msg = "Parsing failed with several errors.\nFirst error %s" % info
 
1298
                error = ConfigObjError(msg)
 
1299
            else:
 
1300
                error = self._errors[0]
 
1301
            # set the errors attribute; it's a list of tuples:
 
1302
            # (error_type, message, line_number)
 
1303
            error.errors = self._errors
 
1304
            # set the config attribute
 
1305
            error.config = self
 
1306
            raise error
 
1307
        # delete private attributes
 
1308
        del self._errors
 
1309
 
 
1310
        if configspec is None:
 
1311
            self.configspec = None
 
1312
        else:
 
1313
            self._handle_configspec(configspec)
 
1314
 
 
1315
 
 
1316
    def _initialise(self, options=None):
 
1317
        if options is None:
 
1318
            options = OPTION_DEFAULTS
 
1319
 
 
1320
        # initialise a few variables
 
1321
        self.filename = None
 
1322
        self._errors = []
 
1323
        self.raise_errors = options['raise_errors']
 
1324
        self.interpolation = options['interpolation']
 
1325
        self.list_values = options['list_values']
 
1326
        self.create_empty = options['create_empty']
 
1327
        self.file_error = options['file_error']
 
1328
        self.stringify = options['stringify']
 
1329
        self.indent_type = options['indent_type']
 
1330
        self.encoding = options['encoding']
 
1331
        self.default_encoding = options['default_encoding']
 
1332
        self.BOM = False
 
1333
        self.newlines = None
 
1334
        self.write_empty_values = options['write_empty_values']
 
1335
        self.unrepr = options['unrepr']
 
1336
 
 
1337
        self.initial_comment = []
 
1338
        self.final_comment = []
 
1339
        self.configspec = None
 
1340
 
 
1341
        if self._inspec:
 
1342
            self.list_values = False
 
1343
 
 
1344
        # Clear section attributes as well
 
1345
        Section._initialise(self)
 
1346
 
 
1347
 
 
1348
    def __repr__(self):
 
1349
        return ('ConfigObj({%s})' %
 
1350
                ', '.join([('%s: %s' % (repr(key), repr(self[key])))
 
1351
                for key in (self.scalars + self.sections)]))
 
1352
 
 
1353
 
 
1354
    def _handle_bom(self, infile):
 
1355
        """
 
1356
        Handle any BOM, and decode if necessary.
 
1357
 
 
1358
        If an encoding is specified, that *must* be used - but the BOM should
 
1359
        still be removed (and the BOM attribute set).
 
1360
 
 
1361
        (If the encoding is wrongly specified, then a BOM for an alternative
 
1362
        encoding won't be discovered or removed.)
 
1363
 
 
1364
        If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
 
1365
        removed. The BOM attribute will be set. UTF16 will be decoded to
 
1366
        unicode.
 
1367
 
 
1368
        NOTE: This method must not be called with an empty ``infile``.
 
1369
 
 
1370
        Specifying the *wrong* encoding is likely to cause a
 
1371
        ``UnicodeDecodeError``.
 
1372
 
 
1373
        ``infile`` must always be returned as a list of lines, but may be
 
1374
        passed in as a single string.
 
1375
        """
 
1376
        if ((self.encoding is not None) and
 
1377
            (self.encoding.lower() not in BOM_LIST)):
 
1378
            # No need to check for a BOM
 
1379
            # the encoding specified doesn't have one
 
1380
            # just decode
 
1381
            return self._decode(infile, self.encoding)
 
1382
 
 
1383
        if isinstance(infile, (list, tuple)):
 
1384
            line = infile[0]
 
1385
        else:
 
1386
            line = infile
 
1387
        if self.encoding is not None:
 
1388
            # encoding explicitly supplied
 
1389
            # And it could have an associated BOM
 
1390
            # TODO: if encoding is just UTF16 - we ought to check for both
 
1391
            # TODO: big endian and little endian versions.
 
1392
            enc = BOM_LIST[self.encoding.lower()]
 
1393
            if enc == 'utf_16':
 
1394
                # For UTF16 we try big endian and little endian
 
1395
                for BOM, (encoding, final_encoding) in BOMS.items():
 
1396
                    if not final_encoding:
 
1397
                        # skip UTF8
 
1398
                        continue
 
1399
                    if infile.startswith(BOM):
 
1400
                        ### BOM discovered
 
1401
                        ##self.BOM = True
 
1402
                        # Don't need to remove BOM
 
1403
                        return self._decode(infile, encoding)
 
1404
 
 
1405
                # If we get this far, will *probably* raise a DecodeError
 
1406
                # As it doesn't appear to start with a BOM
 
1407
                return self._decode(infile, self.encoding)
 
1408
 
 
1409
            # Must be UTF8
 
1410
            BOM = BOM_SET[enc]
 
1411
            if not line.startswith(BOM):
 
1412
                return self._decode(infile, self.encoding)
 
1413
 
 
1414
            newline = line[len(BOM):]
 
1415
 
 
1416
            # BOM removed
 
1417
            if isinstance(infile, (list, tuple)):
 
1418
                infile[0] = newline
 
1419
            else:
 
1420
                infile = newline
 
1421
            self.BOM = True
 
1422
            return self._decode(infile, self.encoding)
 
1423
 
 
1424
        # No encoding specified - so we need to check for UTF8/UTF16
 
1425
        for BOM, (encoding, final_encoding) in BOMS.items():
 
1426
            if not line.startswith(BOM):
 
1427
                continue
 
1428
            else:
 
1429
                # BOM discovered
 
1430
                self.encoding = final_encoding
 
1431
                if not final_encoding:
 
1432
                    self.BOM = True
 
1433
                    # UTF8
 
1434
                    # remove BOM
 
1435
                    newline = line[len(BOM):]
 
1436
                    if isinstance(infile, (list, tuple)):
 
1437
                        infile[0] = newline
 
1438
                    else:
 
1439
                        infile = newline
 
1440
                    # UTF8 - don't decode
 
1441
                    if isinstance(infile, basestring):
 
1442
                        return infile.splitlines(True)
 
1443
                    else:
 
1444
                        return infile
 
1445
                # UTF16 - have to decode
 
1446
                return self._decode(infile, encoding)
 
1447
 
 
1448
        # No BOM discovered and no encoding specified, just return
 
1449
        if isinstance(infile, basestring):
 
1450
            # infile read from a file will be a single string
 
1451
            return infile.splitlines(True)
 
1452
        return infile
 
1453
 
 
1454
 
 
1455
    def _a_to_u(self, aString):
 
1456
        """Decode ASCII strings to unicode if a self.encoding is specified."""
 
1457
        if self.encoding:
 
1458
            return aString.decode('ascii')
 
1459
        else:
 
1460
            return aString
 
1461
 
 
1462
 
 
1463
    def _decode(self, infile, encoding):
 
1464
        """
 
1465
        Decode infile to unicode. Using the specified encoding.
 
1466
 
 
1467
        if is a string, it also needs converting to a list.
 
1468
        """
 
1469
        if isinstance(infile, basestring):
 
1470
            # can't be unicode
 
1471
            # NOTE: Could raise a ``UnicodeDecodeError``
 
1472
            return infile.decode(encoding).splitlines(True)
 
1473
        for i, line in enumerate(infile):
 
1474
            if not isinstance(line, unicode):
 
1475
                # NOTE: The isinstance test here handles mixed lists of unicode/string
 
1476
                # NOTE: But the decode will break on any non-string values
 
1477
                # NOTE: Or could raise a ``UnicodeDecodeError``
 
1478
                infile[i] = line.decode(encoding)
 
1479
        return infile
 
1480
 
 
1481
 
 
1482
    def _decode_element(self, line):
 
1483
        """Decode element to unicode if necessary."""
 
1484
        if not self.encoding:
 
1485
            return line
 
1486
        if isinstance(line, str) and self.default_encoding:
 
1487
            return line.decode(self.default_encoding)
 
1488
        return line
 
1489
 
 
1490
 
 
1491
    def _str(self, value):
 
1492
        """
 
1493
        Used by ``stringify`` within validate, to turn non-string values
 
1494
        into strings.
 
1495
        """
 
1496
        if not isinstance(value, basestring):
 
1497
            return str(value)
 
1498
        else:
 
1499
            return value
 
1500
 
 
1501
 
 
1502
    def _parse(self, infile):
 
1503
        """Actually parse the config file."""
 
1504
        temp_list_values = self.list_values
 
1505
        if self.unrepr:
 
1506
            self.list_values = False
 
1507
 
 
1508
        comment_list = []
 
1509
        done_start = False
 
1510
        this_section = self
 
1511
        maxline = len(infile) - 1
 
1512
        cur_index = -1
 
1513
        reset_comment = False
 
1514
 
 
1515
        while cur_index < maxline:
 
1516
            if reset_comment:
 
1517
                comment_list = []
 
1518
            cur_index += 1
 
1519
            line = infile[cur_index]
 
1520
            sline = line.strip()
 
1521
            # do we have anything on the line ?
 
1522
            if not sline or sline.startswith('#'):
 
1523
                reset_comment = False
 
1524
                comment_list.append(line)
 
1525
                continue
 
1526
 
 
1527
            if not done_start:
 
1528
                # preserve initial comment
 
1529
                self.initial_comment = comment_list
 
1530
                comment_list = []
 
1531
                done_start = True
 
1532
 
 
1533
            reset_comment = True
 
1534
            # first we check if it's a section marker
 
1535
            mat = self._sectionmarker.match(line)
 
1536
            if mat is not None:
 
1537
                # is a section line
 
1538
                (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
 
1539
                if indent and (self.indent_type is None):
 
1540
                    self.indent_type = indent
 
1541
                cur_depth = sect_open.count('[')
 
1542
                if cur_depth != sect_close.count(']'):
 
1543
                    self._handle_error("Cannot compute the section depth at line %s.",
 
1544
                                       NestingError, infile, cur_index)
 
1545
                    continue
 
1546
 
 
1547
                if cur_depth < this_section.depth:
 
1548
                    # the new section is dropping back to a previous level
 
1549
                    try:
 
1550
                        parent = self._match_depth(this_section,
 
1551
                                                   cur_depth).parent
 
1552
                    except SyntaxError:
 
1553
                        self._handle_error("Cannot compute nesting level at line %s.",
 
1554
                                           NestingError, infile, cur_index)
 
1555
                        continue
 
1556
                elif cur_depth == this_section.depth:
 
1557
                    # the new section is a sibling of the current section
 
1558
                    parent = this_section.parent
 
1559
                elif cur_depth == this_section.depth + 1:
 
1560
                    # the new section is a child the current section
 
1561
                    parent = this_section
 
1562
                else:
 
1563
                    self._handle_error("Section too nested at line %s.",
 
1564
                                       NestingError, infile, cur_index)
 
1565
 
 
1566
                sect_name = self._unquote(sect_name)
 
1567
                if sect_name in parent:
 
1568
                    self._handle_error('Duplicate section name at line %s.',
 
1569
                                       DuplicateError, infile, cur_index)
 
1570
                    continue
 
1571
 
 
1572
                # create the new section
 
1573
                this_section = Section(
 
1574
                    parent,
 
1575
                    cur_depth,
 
1576
                    self,
 
1577
                    name=sect_name)
 
1578
                parent[sect_name] = this_section
 
1579
                parent.inline_comments[sect_name] = comment
 
1580
                parent.comments[sect_name] = comment_list
 
1581
                continue
 
1582
            #
 
1583
            # it's not a section marker,
 
1584
            # so it should be a valid ``key = value`` line
 
1585
            mat = self._keyword.match(line)
 
1586
            if mat is None:
 
1587
                # it neither matched as a keyword
 
1588
                # or a section marker
 
1589
                self._handle_error(
 
1590
                    'Invalid line at line "%s".',
 
1591
                    ParseError, infile, cur_index)
 
1592
            else:
 
1593
                # is a keyword value
 
1594
                # value will include any inline comment
 
1595
                (indent, key, value) = mat.groups()
 
1596
                if indent and (self.indent_type is None):
 
1597
                    self.indent_type = indent
 
1598
                # check for a multiline value
 
1599
                if value[:3] in ['"""', "'''"]:
 
1600
                    try:
 
1601
                        (value, comment, cur_index) = self._multiline(
 
1602
                            value, infile, cur_index, maxline)
 
1603
                    except SyntaxError:
 
1604
                        self._handle_error(
 
1605
                            'Parse error in value at line %s.',
 
1606
                            ParseError, infile, cur_index)
 
1607
                        continue
 
1608
                    else:
 
1609
                        if self.unrepr:
 
1610
                            comment = ''
 
1611
                            try:
 
1612
                                value = unrepr(value)
 
1613
                            except Exception, e:
 
1614
                                if type(e) == UnknownType:
 
1615
                                    msg = 'Unknown name or type in value at line %s.'
 
1616
                                else:
 
1617
                                    msg = 'Parse error in value at line %s.'
 
1618
                                self._handle_error(msg, UnreprError, infile,
 
1619
                                    cur_index)
 
1620
                                continue
 
1621
                else:
 
1622
                    if self.unrepr:
 
1623
                        comment = ''
 
1624
                        try:
 
1625
                            value = unrepr(value)
 
1626
                        except Exception, e:
 
1627
                            if isinstance(e, UnknownType):
 
1628
                                msg = 'Unknown name or type in value at line %s.'
 
1629
                            else:
 
1630
                                msg = 'Parse error in value at line %s.'
 
1631
                            self._handle_error(msg, UnreprError, infile,
 
1632
                                cur_index)
 
1633
                            continue
 
1634
                    else:
 
1635
                        # extract comment and lists
 
1636
                        try:
 
1637
                            (value, comment) = self._handle_value(value)
 
1638
                        except SyntaxError:
 
1639
                            self._handle_error(
 
1640
                                'Parse error in value at line %s.',
 
1641
                                ParseError, infile, cur_index)
 
1642
                            continue
 
1643
                #
 
1644
                key = self._unquote(key)
 
1645
                if key in this_section:
 
1646
                    self._handle_error(
 
1647
                        'Duplicate keyword name at line %s.',
 
1648
                        DuplicateError, infile, cur_index)
 
1649
                    continue
 
1650
                # add the key.
 
1651
                # we set unrepr because if we have got this far we will never
 
1652
                # be creating a new section
 
1653
                this_section.__setitem__(key, value, unrepr=True)
 
1654
                this_section.inline_comments[key] = comment
 
1655
                this_section.comments[key] = comment_list
 
1656
                continue
 
1657
        #
 
1658
        if self.indent_type is None:
 
1659
            # no indentation used, set the type accordingly
 
1660
            self.indent_type = ''
 
1661
 
 
1662
        # preserve the final comment
 
1663
        if not self and not self.initial_comment:
 
1664
            self.initial_comment = comment_list
 
1665
        elif not reset_comment:
 
1666
            self.final_comment = comment_list
 
1667
        self.list_values = temp_list_values
 
1668
 
 
1669
 
 
1670
    def _match_depth(self, sect, depth):
 
1671
        """
 
1672
        Given a section and a depth level, walk back through the sections
 
1673
        parents to see if the depth level matches a previous section.
 
1674
 
 
1675
        Return a reference to the right section,
 
1676
        or raise a SyntaxError.
 
1677
        """
 
1678
        while depth < sect.depth:
 
1679
            if sect is sect.parent:
 
1680
                # we've reached the top level already
 
1681
                raise SyntaxError()
 
1682
            sect = sect.parent
 
1683
        if sect.depth == depth:
 
1684
            return sect
 
1685
        # shouldn't get here
 
1686
        raise SyntaxError()
 
1687
 
 
1688
 
 
1689
    def _handle_error(self, text, ErrorClass, infile, cur_index):
 
1690
        """
 
1691
        Handle an error according to the error settings.
 
1692
 
 
1693
        Either raise the error or store it.
 
1694
        The error will have occured at ``cur_index``
 
1695
        """
 
1696
        line = infile[cur_index]
 
1697
        cur_index += 1
 
1698
        message = text % cur_index
 
1699
        error = ErrorClass(message, cur_index, line)
 
1700
        if self.raise_errors:
 
1701
            # raise the error - parsing stops here
 
1702
            raise error
 
1703
        # store the error
 
1704
        # reraise when parsing has finished
 
1705
        self._errors.append(error)
 
1706
 
 
1707
 
 
1708
    def _unquote(self, value):
 
1709
        """Return an unquoted version of a value"""
 
1710
        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
 
1711
            value = value[1:-1]
 
1712
        return value
 
1713
 
 
1714
 
 
1715
    def _quote(self, value, multiline=True):
 
1716
        """
 
1717
        Return a safely quoted version of a value.
 
1718
 
 
1719
        Raise a ConfigObjError if the value cannot be safely quoted.
 
1720
        If multiline is ``True`` (default) then use triple quotes
 
1721
        if necessary.
 
1722
 
 
1723
        * Don't quote values that don't need it.
 
1724
        * Recursively quote members of a list and return a comma joined list.
 
1725
        * Multiline is ``False`` for lists.
 
1726
        * Obey list syntax for empty and single member lists.
 
1727
 
 
1728
        If ``list_values=False`` then the value is only quoted if it contains
 
1729
        a ``\\n`` (is multiline) or '#'.
 
1730
 
 
1731
        If ``write_empty_values`` is set, and the value is an empty string, it
 
1732
        won't be quoted.
 
1733
        """
 
1734
        if multiline and self.write_empty_values and value == '':
 
1735
            # Only if multiline is set, so that it is used for values not
 
1736
            # keys, and not values that are part of a list
 
1737
            return ''
 
1738
 
 
1739
        if multiline and isinstance(value, (list, tuple)):
 
1740
            if not value:
 
1741
                return ','
 
1742
            elif len(value) == 1:
 
1743
                return self._quote(value[0], multiline=False) + ','
 
1744
            return ', '.join([self._quote(val, multiline=False)
 
1745
                for val in value])
 
1746
        if not isinstance(value, basestring):
 
1747
            if self.stringify:
 
1748
                value = str(value)
 
1749
            else:
 
1750
                raise TypeError('Value "%s" is not a string.' % value)
 
1751
 
 
1752
        if not value:
 
1753
            return '""'
 
1754
 
 
1755
        no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
 
1756
        need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
 
1757
        hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
 
1758
        check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
 
1759
 
 
1760
        if check_for_single:
 
1761
            if not self.list_values:
 
1762
                # we don't quote if ``list_values=False``
 
1763
                quot = noquot
 
1764
            # for normal values either single or double quotes will do
 
1765
            elif '\n' in value:
 
1766
                # will only happen if multiline is off - e.g. '\n' in key
 
1767
                raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
 
1768
            elif ((value[0] not in wspace_plus) and
 
1769
                    (value[-1] not in wspace_plus) and
 
1770
                    (',' not in value)):
 
1771
                quot = noquot
 
1772
            else:
 
1773
                quot = self._get_single_quote(value)
 
1774
        else:
 
1775
            # if value has '\n' or "'" *and* '"', it will need triple quotes
 
1776
            quot = self._get_triple_quote(value)
 
1777
 
 
1778
        if quot == noquot and '#' in value and self.list_values:
 
1779
            quot = self._get_single_quote(value)
 
1780
 
 
1781
        return quot % value
 
1782
 
 
1783
 
 
1784
    def _get_single_quote(self, value):
 
1785
        if ("'" in value) and ('"' in value):
 
1786
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
 
1787
        elif '"' in value:
 
1788
            quot = squot
 
1789
        else:
 
1790
            quot = dquot
 
1791
        return quot
 
1792
 
 
1793
 
 
1794
    def _get_triple_quote(self, value):
 
1795
        if (value.find('"""') != -1) and (value.find("'''") != -1):
 
1796
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
 
1797
        # upstream version (up to version 4.7.2) has the bug with incorrect quoting;
 
1798
        # fixed in our copy based on the suggestion of ConfigObj's author
 
1799
        if value.find('"""') == -1:
 
1800
            quot = tsquot
 
1801
        else:
 
1802
            quot = tdquot
 
1803
        return quot
 
1804
 
 
1805
 
 
1806
    def _handle_value(self, value):
 
1807
        """
 
1808
        Given a value string, unquote, remove comment,
 
1809
        handle lists. (including empty and single member lists)
 
1810
        """
 
1811
        if self._inspec:
 
1812
            # Parsing a configspec so don't handle comments
 
1813
            return (value, '')
 
1814
        # do we look for lists in values ?
 
1815
        if not self.list_values:
 
1816
            mat = self._nolistvalue.match(value)
 
1817
            if mat is None:
 
1818
                raise SyntaxError()
 
1819
            # NOTE: we don't unquote here
 
1820
            return mat.groups()
 
1821
        #
 
1822
        mat = self._valueexp.match(value)
 
1823
        if mat is None:
 
1824
            # the value is badly constructed, probably badly quoted,
 
1825
            # or an invalid list
 
1826
            raise SyntaxError()
 
1827
        (list_values, single, empty_list, comment) = mat.groups()
 
1828
        if (list_values == '') and (single is None):
 
1829
            # change this if you want to accept empty values
 
1830
            raise SyntaxError()
 
1831
        # NOTE: note there is no error handling from here if the regex
 
1832
        # is wrong: then incorrect values will slip through
 
1833
        if empty_list is not None:
 
1834
            # the single comma - meaning an empty list
 
1835
            return ([], comment)
 
1836
        if single is not None:
 
1837
            # handle empty values
 
1838
            if list_values and not single:
 
1839
                # FIXME: the '' is a workaround because our regex now matches
 
1840
                #   '' at the end of a list if it has a trailing comma
 
1841
                single = None
 
1842
            else:
 
1843
                single = single or '""'
 
1844
                single = self._unquote(single)
 
1845
        if list_values == '':
 
1846
            # not a list value
 
1847
            return (single, comment)
 
1848
        the_list = self._listvalueexp.findall(list_values)
 
1849
        the_list = [self._unquote(val) for val in the_list]
 
1850
        if single is not None:
 
1851
            the_list += [single]
 
1852
        return (the_list, comment)
 
1853
 
 
1854
 
 
1855
    def _multiline(self, value, infile, cur_index, maxline):
 
1856
        """Extract the value, where we are in a multiline situation."""
 
1857
        quot = value[:3]
 
1858
        newvalue = value[3:]
 
1859
        single_line = self._triple_quote[quot][0]
 
1860
        multi_line = self._triple_quote[quot][1]
 
1861
        mat = single_line.match(value)
 
1862
        if mat is not None:
 
1863
            retval = list(mat.groups())
 
1864
            retval.append(cur_index)
 
1865
            return retval
 
1866
        elif newvalue.find(quot) != -1:
 
1867
            # somehow the triple quote is missing
 
1868
            raise SyntaxError()
 
1869
        #
 
1870
        while cur_index < maxline:
 
1871
            cur_index += 1
 
1872
            newvalue += '\n'
 
1873
            line = infile[cur_index]
 
1874
            if line.find(quot) == -1:
 
1875
                newvalue += line
 
1876
            else:
 
1877
                # end of multiline, process it
 
1878
                break
 
1879
        else:
 
1880
            # we've got to the end of the config, oops...
 
1881
            raise SyntaxError()
 
1882
        mat = multi_line.match(line)
 
1883
        if mat is None:
 
1884
            # a badly formed line
 
1885
            raise SyntaxError()
 
1886
        (value, comment) = mat.groups()
 
1887
        return (newvalue + value, comment, cur_index)
 
1888
 
 
1889
 
 
1890
    def _handle_configspec(self, configspec):
 
1891
        """Parse the configspec."""
 
1892
        # FIXME: Should we check that the configspec was created with the
 
1893
        #        correct settings ? (i.e. ``list_values=False``)
 
1894
        if not isinstance(configspec, ConfigObj):
 
1895
            try:
 
1896
                configspec = ConfigObj(configspec,
 
1897
                                       raise_errors=True,
 
1898
                                       file_error=True,
 
1899
                                       _inspec=True)
 
1900
            except ConfigObjError, e:
 
1901
                # FIXME: Should these errors have a reference
 
1902
                #        to the already parsed ConfigObj ?
 
1903
                raise ConfigspecError('Parsing configspec failed: %s' % e)
 
1904
            except IOError, e:
 
1905
                raise IOError('Reading configspec failed: %s' % e)
 
1906
 
 
1907
        self.configspec = configspec
 
1908
 
 
1909
 
 
1910
 
 
1911
    def _set_configspec(self, section, copy):
 
1912
        """
 
1913
        Called by validate. Handles setting the configspec on subsections
 
1914
        including sections to be validated by __many__
 
1915
        """
 
1916
        configspec = section.configspec
 
1917
        many = configspec.get('__many__')
 
1918
        if isinstance(many, dict):
 
1919
            for entry in section.sections:
 
1920
                if entry not in configspec:
 
1921
                    section[entry].configspec = many
 
1922
 
 
1923
        for entry in configspec.sections:
 
1924
            if entry == '__many__':
 
1925
                continue
 
1926
            if entry not in section:
 
1927
                section[entry] = {}
 
1928
                if copy:
 
1929
                    # copy comments
 
1930
                    section.comments[entry] = configspec.comments.get(entry, [])
 
1931
                    section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
 
1932
 
 
1933
            # Could be a scalar when we expect a section
 
1934
            if isinstance(section[entry], Section):
 
1935
                section[entry].configspec = configspec[entry]
 
1936
 
 
1937
 
 
1938
    def _write_line(self, indent_string, entry, this_entry, comment):
 
1939
        """Write an individual line, for the write method"""
 
1940
        # NOTE: the calls to self._quote here handles non-StringType values.
 
1941
        if not self.unrepr:
 
1942
            val = self._decode_element(self._quote(this_entry))
 
1943
        else:
 
1944
            val = repr(this_entry)
 
1945
        return '%s%s%s%s%s' % (indent_string,
 
1946
                               self._decode_element(self._quote(entry, multiline=False)),
 
1947
                               self._a_to_u(' = '),
 
1948
                               val,
 
1949
                               self._decode_element(comment))
 
1950
 
 
1951
 
 
1952
    def _write_marker(self, indent_string, depth, entry, comment):
 
1953
        """Write a section marker line"""
 
1954
        return '%s%s%s%s%s' % (indent_string,
 
1955
                               self._a_to_u('[' * depth),
 
1956
                               self._quote(self._decode_element(entry), multiline=False),
 
1957
                               self._a_to_u(']' * depth),
 
1958
                               self._decode_element(comment))
 
1959
 
 
1960
 
 
1961
    def _handle_comment(self, comment):
 
1962
        """Deal with a comment."""
 
1963
        if not comment:
 
1964
            return ''
 
1965
        start = self.indent_type
 
1966
        if not comment.startswith('#'):
 
1967
            start += self._a_to_u(' # ')
 
1968
        return (start + comment)
 
1969
 
 
1970
 
 
1971
    # Public methods
 
1972
 
 
1973
    def write(self, outfile=None, section=None):
 
1974
        """
 
1975
        Write the current ConfigObj as a file
 
1976
 
 
1977
        tekNico: FIXME: use StringIO instead of real files
 
1978
 
 
1979
        >>> filename = a.filename
 
1980
        >>> a.filename = 'test.ini'
 
1981
        >>> a.write()
 
1982
        >>> a.filename = filename
 
1983
        >>> a == ConfigObj('test.ini', raise_errors=True)
 
1984
        1
 
1985
        """
 
1986
        if self.indent_type is None:
 
1987
            # this can be true if initialised from a dictionary
 
1988
            self.indent_type = DEFAULT_INDENT_TYPE
 
1989
 
 
1990
        out = []
 
1991
        cs = self._a_to_u('#')
 
1992
        csp = self._a_to_u('# ')
 
1993
        if section is None:
 
1994
            int_val = self.interpolation
 
1995
            self.interpolation = False
 
1996
            section = self
 
1997
            for line in self.initial_comment:
 
1998
                line = self._decode_element(line)
 
1999
                stripped_line = line.strip()
 
2000
                if stripped_line and not stripped_line.startswith(cs):
 
2001
                    line = csp + line
 
2002
                out.append(line)
 
2003
 
 
2004
        indent_string = self.indent_type * section.depth
 
2005
        for entry in (section.scalars + section.sections):
 
2006
            if entry in section.defaults:
 
2007
                # don't write out default values
 
2008
                continue
 
2009
            for comment_line in section.comments[entry]:
 
2010
                comment_line = self._decode_element(comment_line.lstrip())
 
2011
                if comment_line and not comment_line.startswith(cs):
 
2012
                    comment_line = csp + comment_line
 
2013
                out.append(indent_string + comment_line)
 
2014
            this_entry = section[entry]
 
2015
            comment = self._handle_comment(section.inline_comments[entry])
 
2016
 
 
2017
            if isinstance(this_entry, dict):
 
2018
                # a section
 
2019
                out.append(self._write_marker(
 
2020
                    indent_string,
 
2021
                    this_entry.depth,
 
2022
                    entry,
 
2023
                    comment))
 
2024
                out.extend(self.write(section=this_entry))
 
2025
            else:
 
2026
                out.append(self._write_line(
 
2027
                    indent_string,
 
2028
                    entry,
 
2029
                    this_entry,
 
2030
                    comment))
 
2031
 
 
2032
        if section is self:
 
2033
            for line in self.final_comment:
 
2034
                line = self._decode_element(line)
 
2035
                stripped_line = line.strip()
 
2036
                if stripped_line and not stripped_line.startswith(cs):
 
2037
                    line = csp + line
 
2038
                out.append(line)
 
2039
            self.interpolation = int_val
 
2040
 
 
2041
        if section is not self:
 
2042
            return out
 
2043
 
 
2044
        if (self.filename is None) and (outfile is None):
 
2045
            # output a list of lines
 
2046
            # might need to encode
 
2047
            # NOTE: This will *screw* UTF16, each line will start with the BOM
 
2048
            if self.encoding:
 
2049
                out = [l.encode(self.encoding) for l in out]
 
2050
            if (self.BOM and ((self.encoding is None) or
 
2051
                (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
 
2052
                # Add the UTF8 BOM
 
2053
                if not out:
 
2054
                    out.append('')
 
2055
                out[0] = BOM_UTF8 + out[0]
 
2056
            return out
 
2057
 
 
2058
        # Turn the list to a string, joined with correct newlines
 
2059
        newline = self.newlines or os.linesep
 
2060
        output = self._a_to_u(newline).join(out)
 
2061
        if self.encoding:
 
2062
            output = output.encode(self.encoding)
 
2063
        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
 
2064
            # Add the UTF8 BOM
 
2065
            output = BOM_UTF8 + output
 
2066
 
 
2067
        if not output.endswith(newline):
 
2068
            output += newline
 
2069
        if outfile is not None:
 
2070
            outfile.write(output)
 
2071
        else:
 
2072
            h = open(self.filename, 'wb')
 
2073
            h.write(output)
 
2074
            h.close()
 
2075
 
 
2076
 
 
2077
    def validate(self, validator, preserve_errors=False, copy=False,
 
2078
                 section=None):
 
2079
        """
 
2080
        Test the ConfigObj against a configspec.
 
2081
 
 
2082
        It uses the ``validator`` object from *validate.py*.
 
2083
 
 
2084
        To run ``validate`` on the current ConfigObj, call: ::
 
2085
 
 
2086
            test = config.validate(validator)
 
2087
 
 
2088
        (Normally having previously passed in the configspec when the ConfigObj
 
2089
        was created - you can dynamically assign a dictionary of checks to the
 
2090
        ``configspec`` attribute of a section though).
 
2091
 
 
2092
        It returns ``True`` if everything passes, or a dictionary of
 
2093
        pass/fails (True/False). If every member of a subsection passes, it
 
2094
        will just have the value ``True``. (It also returns ``False`` if all
 
2095
        members fail).
 
2096
 
 
2097
        In addition, it converts the values from strings to their native
 
2098
        types if their checks pass (and ``stringify`` is set).
 
2099
 
 
2100
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
 
2101
        of a marking a fail with a ``False``, it will preserve the actual
 
2102
        exception object. This can contain info about the reason for failure.
 
2103
        For example the ``VdtValueTooSmallError`` indicates that the value
 
2104
        supplied was too small. If a value (or section) is missing it will
 
2105
        still be marked as ``False``.
 
2106
 
 
2107
        You must have the validate module to use ``preserve_errors=True``.
 
2108
 
 
2109
        You can then use the ``flatten_errors`` function to turn your nested
 
2110
        results dictionary into a flattened list of failures - useful for
 
2111
        displaying meaningful error messages.
 
2112
        """
 
2113
        if section is None:
 
2114
            if self.configspec is None:
 
2115
                raise ValueError('No configspec supplied.')
 
2116
            if preserve_errors:
 
2117
                # We do this once to remove a top level dependency on the validate module
 
2118
                # Which makes importing configobj faster
 
2119
                from validate import VdtMissingValue
 
2120
                self._vdtMissingValue = VdtMissingValue
 
2121
 
 
2122
            section = self
 
2123
 
 
2124
            if copy:
 
2125
                section.initial_comment = section.configspec.initial_comment
 
2126
                section.final_comment = section.configspec.final_comment
 
2127
                section.encoding = section.configspec.encoding
 
2128
                section.BOM = section.configspec.BOM
 
2129
                section.newlines = section.configspec.newlines
 
2130
                section.indent_type = section.configspec.indent_type
 
2131
 
 
2132
        #
 
2133
        configspec = section.configspec
 
2134
        self._set_configspec(section, copy)
 
2135
 
 
2136
        def validate_entry(entry, spec, val, missing, ret_true, ret_false):
 
2137
            try:
 
2138
                check = validator.check(spec,
 
2139
                                        val,
 
2140
                                        missing=missing
 
2141
                                        )
 
2142
            except validator.baseErrorClass, e:
 
2143
                if not preserve_errors or isinstance(e, self._vdtMissingValue):
 
2144
                    out[entry] = False
 
2145
                else:
 
2146
                    # preserve the error
 
2147
                    out[entry] = e
 
2148
                    ret_false = False
 
2149
                ret_true = False
 
2150
            else:
 
2151
                try:
 
2152
                    section.default_values.pop(entry, None)
 
2153
                except AttributeError:
 
2154
                    # For Python 2.2 compatibility
 
2155
                    try:
 
2156
                        del section.default_values[entry]
 
2157
                    except KeyError:
 
2158
                        pass
 
2159
 
 
2160
                try:
 
2161
                    section.default_values[entry] = validator.get_default_value(configspec[entry])
 
2162
                except (KeyError, AttributeError):
 
2163
                    # No default or validator has no 'get_default_value' (e.g. SimpleVal)
 
2164
                    pass
 
2165
 
 
2166
                ret_false = False
 
2167
                out[entry] = True
 
2168
                if self.stringify or missing:
 
2169
                    # if we are doing type conversion
 
2170
                    # or the value is a supplied default
 
2171
                    if not self.stringify:
 
2172
                        if isinstance(check, (list, tuple)):
 
2173
                            # preserve lists
 
2174
                            check = [self._str(item) for item in check]
 
2175
                        elif missing and check is None:
 
2176
                            # convert the None from a default to a ''
 
2177
                            check = ''
 
2178
                        else:
 
2179
                            check = self._str(check)
 
2180
                    if (check != val) or missing:
 
2181
                        section[entry] = check
 
2182
                if not copy and missing and entry not in section.defaults:
 
2183
                    section.defaults.append(entry)
 
2184
            return ret_true, ret_false
 
2185
 
 
2186
        #
 
2187
        out = {}
 
2188
        ret_true = True
 
2189
        ret_false = True
 
2190
 
 
2191
        unvalidated = [k for k in section.scalars if k not in configspec]
 
2192
        incorrect_sections = [k for k in configspec.sections if k in section.scalars]
 
2193
        incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
 
2194
 
 
2195
        for entry in configspec.scalars:
 
2196
            if entry in ('__many__', '___many___'):
 
2197
                # reserved names
 
2198
                continue
 
2199
 
 
2200
            if (not entry in section.scalars) or (entry in section.defaults):
 
2201
                # missing entries
 
2202
                # or entries from defaults
 
2203
                missing = True
 
2204
                val = None
 
2205
                if copy and not entry in section.scalars:
 
2206
                    # copy comments
 
2207
                    section.comments[entry] = (
 
2208
                        configspec.comments.get(entry, []))
 
2209
                    section.inline_comments[entry] = (
 
2210
                        configspec.inline_comments.get(entry, ''))
 
2211
                #
 
2212
            else:
 
2213
                missing = False
 
2214
                val = section[entry]
 
2215
 
 
2216
            ret_true, ret_false = validate_entry(entry, configspec[entry], val,
 
2217
                                                 missing, ret_true, ret_false)
 
2218
 
 
2219
        many = None
 
2220
        if '__many__' in configspec.scalars:
 
2221
            many = configspec['__many__']
 
2222
        elif '___many___' in configspec.scalars:
 
2223
            many = configspec['___many___']
 
2224
 
 
2225
        if many is not None:
 
2226
            for entry in unvalidated:
 
2227
                val = section[entry]
 
2228
                ret_true, ret_false = validate_entry(entry, many, val, False,
 
2229
                                                     ret_true, ret_false)
 
2230
 
 
2231
        for entry in incorrect_scalars:
 
2232
            ret_true = False
 
2233
            if not preserve_errors:
 
2234
                out[entry] = False
 
2235
            else:
 
2236
                ret_false = False
 
2237
                msg = 'Value %r was provided as a section' % entry
 
2238
                out[entry] = validator.baseErrorClass(msg)
 
2239
        for entry in incorrect_sections:
 
2240
            ret_true = False
 
2241
            if not preserve_errors:
 
2242
                out[entry] = False
 
2243
            else:
 
2244
                ret_false = False
 
2245
                msg = 'Section %r was provided as a single value' % entry
 
2246
                out[entry] = validator.baseErrorClass(msg)
 
2247
 
 
2248
        # Missing sections will have been created as empty ones when the
 
2249
        # configspec was read.
 
2250
        for entry in section.sections:
 
2251
            # FIXME: this means DEFAULT is not copied in copy mode
 
2252
            if section is self and entry == 'DEFAULT':
 
2253
                continue
 
2254
            if section[entry].configspec is None:
 
2255
                continue
 
2256
            if copy:
 
2257
                section.comments[entry] = configspec.comments.get(entry, [])
 
2258
                section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
 
2259
            check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
 
2260
            out[entry] = check
 
2261
            if check == False:
 
2262
                ret_true = False
 
2263
            elif check == True:
 
2264
                ret_false = False
 
2265
            else:
 
2266
                ret_true = False
 
2267
                ret_false = False
 
2268
        #
 
2269
        if ret_true:
 
2270
            return True
 
2271
        elif ret_false:
 
2272
            return False
 
2273
        return out
 
2274
 
 
2275
 
 
2276
    def reset(self):
 
2277
        """Clear ConfigObj instance and restore to 'freshly created' state."""
 
2278
        self.clear()
 
2279
        self._initialise()
 
2280
        # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
 
2281
        #        requires an empty dictionary
 
2282
        self.configspec = None
 
2283
        # Just to be sure ;-)
 
2284
        self._original_configspec = None
 
2285
 
 
2286
 
 
2287
    def reload(self):
 
2288
        """
 
2289
        Reload a ConfigObj from file.
 
2290
 
 
2291
        This method raises a ``ReloadError`` if the ConfigObj doesn't have
 
2292
        a filename attribute pointing to a file.
 
2293
        """
 
2294
        if not isinstance(self.filename, basestring):
 
2295
            raise ReloadError()
 
2296
 
 
2297
        filename = self.filename
 
2298
        current_options = {}
 
2299
        for entry in OPTION_DEFAULTS:
 
2300
            if entry == 'configspec':
 
2301
                continue
 
2302
            current_options[entry] = getattr(self, entry)
 
2303
 
 
2304
        configspec = self._original_configspec
 
2305
        current_options['configspec'] = configspec
 
2306
 
 
2307
        self.clear()
 
2308
        self._initialise(current_options)
 
2309
        self._load(filename, configspec)
 
2310
 
 
2311
 
 
2312
 
 
2313
class SimpleVal(object):
 
2314
    """
 
2315
    A simple validator.
 
2316
    Can be used to check that all members expected are present.
 
2317
 
 
2318
    To use it, provide a configspec with all your members in (the value given
 
2319
    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
 
2320
    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
 
2321
    members are present, or a dictionary with True/False meaning
 
2322
    present/missing. (Whole missing sections will be replaced with ``False``)
 
2323
    """
 
2324
 
 
2325
    def __init__(self):
 
2326
        self.baseErrorClass = ConfigObjError
 
2327
 
 
2328
    def check(self, check, member, missing=False):
 
2329
        """A dummy check method, always returns the value unchanged."""
 
2330
        if missing:
 
2331
            raise self.baseErrorClass()
 
2332
        return member
 
2333
 
 
2334
 
 
2335
# Check / processing functions for options
 
2336
def flatten_errors(cfg, res, levels=None, results=None):
 
2337
    """
 
2338
    An example function that will turn a nested dictionary of results
 
2339
    (as returned by ``ConfigObj.validate``) into a flat list.
 
2340
 
 
2341
    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
 
2342
    dictionary returned by ``validate``.
 
2343
 
 
2344
    (This is a recursive function, so you shouldn't use the ``levels`` or
 
2345
    ``results`` arguments - they are used by the function.)
 
2346
 
 
2347
    Returns a list of keys that failed. Each member of the list is a tuple :
 
2348
 
 
2349
    ::
 
2350
 
 
2351
        ([list of sections...], key, result)
 
2352
 
 
2353
    If ``validate`` was called with ``preserve_errors=False`` (the default)
 
2354
    then ``result`` will always be ``False``.
 
2355
 
 
2356
    *list of sections* is a flattened list of sections that the key was found
 
2357
    in.
 
2358
 
 
2359
    If the section was missing (or a section was expected and a scalar provided
 
2360
    - or vice-versa) then key will be ``None``.
 
2361
 
 
2362
    If the value (or section) was missing then ``result`` will be ``False``.
 
2363
 
 
2364
    If ``validate`` was called with ``preserve_errors=True`` and a value
 
2365
    was present, but failed the check, then ``result`` will be the exception
 
2366
    object returned. You can use this as a string that describes the failure.
 
2367
 
 
2368
    For example *The value "3" is of the wrong type*.
 
2369
 
 
2370
    >>> import validate
 
2371
    >>> vtor = validate.Validator()
 
2372
    >>> my_ini = '''
 
2373
    ...     option1 = True
 
2374
    ...     [section1]
 
2375
    ...     option1 = True
 
2376
    ...     [section2]
 
2377
    ...     another_option = Probably
 
2378
    ...     [section3]
 
2379
    ...     another_option = True
 
2380
    ...     [[section3b]]
 
2381
    ...     value = 3
 
2382
    ...     value2 = a
 
2383
    ...     value3 = 11
 
2384
    ...     '''
 
2385
    >>> my_cfg = '''
 
2386
    ...     option1 = boolean()
 
2387
    ...     option2 = boolean()
 
2388
    ...     option3 = boolean(default=Bad_value)
 
2389
    ...     [section1]
 
2390
    ...     option1 = boolean()
 
2391
    ...     option2 = boolean()
 
2392
    ...     option3 = boolean(default=Bad_value)
 
2393
    ...     [section2]
 
2394
    ...     another_option = boolean()
 
2395
    ...     [section3]
 
2396
    ...     another_option = boolean()
 
2397
    ...     [[section3b]]
 
2398
    ...     value = integer
 
2399
    ...     value2 = integer
 
2400
    ...     value3 = integer(0, 10)
 
2401
    ...         [[[section3b-sub]]]
 
2402
    ...         value = string
 
2403
    ...     [section4]
 
2404
    ...     another_option = boolean()
 
2405
    ...     '''
 
2406
    >>> cs = my_cfg.split('\\n')
 
2407
    >>> ini = my_ini.split('\\n')
 
2408
    >>> cfg = ConfigObj(ini, configspec=cs)
 
2409
    >>> res = cfg.validate(vtor, preserve_errors=True)
 
2410
    >>> errors = []
 
2411
    >>> for entry in flatten_errors(cfg, res):
 
2412
    ...     section_list, key, error = entry
 
2413
    ...     section_list.insert(0, '[root]')
 
2414
    ...     if key is not None:
 
2415
    ...        section_list.append(key)
 
2416
    ...     else:
 
2417
    ...         section_list.append('[missing]')
 
2418
    ...     section_string = ', '.join(section_list)
 
2419
    ...     errors.append((section_string, ' = ', error))
 
2420
    >>> errors.sort()
 
2421
    >>> for entry in errors:
 
2422
    ...     print entry[0], entry[1], (entry[2] or 0)
 
2423
    [root], option2  =  0
 
2424
    [root], option3  =  the value "Bad_value" is of the wrong type.
 
2425
    [root], section1, option2  =  0
 
2426
    [root], section1, option3  =  the value "Bad_value" is of the wrong type.
 
2427
    [root], section2, another_option  =  the value "Probably" is of the wrong type.
 
2428
    [root], section3, section3b, section3b-sub, [missing]  =  0
 
2429
    [root], section3, section3b, value2  =  the value "a" is of the wrong type.
 
2430
    [root], section3, section3b, value3  =  the value "11" is too big.
 
2431
    [root], section4, [missing]  =  0
 
2432
    """
 
2433
    if levels is None:
 
2434
        # first time called
 
2435
        levels = []
 
2436
        results = []
 
2437
    if res is True:
 
2438
        return results
 
2439
    if res is False or isinstance(res, Exception):
 
2440
        results.append((levels[:], None, res))
 
2441
        if levels:
 
2442
            levels.pop()
 
2443
        return results
 
2444
    for (key, val) in res.items():
 
2445
        if val == True:
 
2446
            continue
 
2447
        if isinstance(cfg.get(key), dict):
 
2448
            # Go down one level
 
2449
            levels.append(key)
 
2450
            flatten_errors(cfg[key], val, levels, results)
 
2451
            continue
 
2452
        results.append((levels[:], key, val))
 
2453
    #
 
2454
    # Go up one level
 
2455
    if levels:
 
2456
        levels.pop()
 
2457
    #
 
2458
    return results
 
2459
 
 
2460
 
 
2461
"""*A programming language is a medium of expression.* - Paul Graham"""