~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Alexander Belchenko
  • Date: 2008-03-11 08:49:42 UTC
  • mto: This revision was merged to the branch mainline in revision 3268.
  • Revision ID: bialix@ukr.net-20080311084942-w1w0w3v0m20p2pbc
use sys.version_info

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