~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: John Arbash Meinel
  • Date: 2005-09-29 21:13:03 UTC
  • mto: (1393.1.12)
  • mto: This revision was merged to the branch mainline in revision 1396.
  • Revision ID: john@arbash-meinel.com-20050929211303-7f1f9bf969d65dc3
All tests pass.

Show diffs side-by-side

added added

removed removed

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