/brz/remove-bazaar

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