~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Robert Collins
  • Date: 2005-09-28 05:25:54 UTC
  • mfrom: (1185.1.42)
  • mto: (1092.2.18)
  • mto: This revision was merged to the branch mainline in revision 1397.
  • Revision ID: robertc@robertcollins.net-20050928052554-beb985505f77ea6a
update symlink branch to integration

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"""