~ubuntu-branches/ubuntu/trusty/configobj/trusty

« back to all changes in this revision

Viewing changes to .pc/triplequotes.diff/configobj.py

  • Committer: Bazaar Package Importer
  • Author(s): Jelmer Vernooij, Stefano Rivera, Jelmer Vernooij
  • Date: 2011-03-16 11:17:14 UTC
  • Revision ID: james.westby@ubuntu.com-20110316111714-p0199jx0lfyd5az9
Tags: 4.7.2+ds-2
[ Stefano Rivera ]
* Don't leak uid and umask into source tarball and set -e.
* Bumped Standards-Version to 3.9.1 (no changes needed).
* Enable test suites.
  - Build Depend on python-all, python-unittest2.
  - New patch: report-doctest-failure.diff: Fail on failures.
  - New patch: py27-test.diff: Convert float-comparing doctests to unit
    tests.
* Wrap long lines in debian/control.
* Merge Build-Depends-Indep into Build-Depends (no arch-dependant packages).
* Switch to dh_python2
  - Use X-Python-Version (requires python-all >= 2.6.5-13~).
  - Use ${python:Breaks}.
* Update my e-mail address.
* Use DEP5 format in debian/copyright.

[ Jelmer Vernooij ]
* Properly handle triple quotes. Closes: #618349, LP: #710410
* Add myself to uploaders.

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