~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Vincent Ladeuil
  • Date: 2013-08-09 15:09:23 UTC
  • mto: This revision was merged to the branch mainline in revision 6587.
  • Revision ID: v.ladeuil+lp@free.fr-20130809150923-y71dusyorep0hbkt
Fix various typos in docstrings. Rename 'buffer' to 'buf' since it's now a python builtin function.

Show diffs side-by-side

added added

removed removed

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