/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: Andrew Bennetts
  • Date: 2008-09-08 13:44:19 UTC
  • mto: This revision was merged to the branch mainline in revision 3756.
  • Revision ID: andrew.bennetts@canonical.com-20080908134419-y8jrlhi4k4fvwaj0
More minor import tidying suggested by pyflakes.

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