~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Robert Collins
  • Date: 2005-12-02 03:23:47 UTC
  • Revision ID: robertc@robertcollins.net-20051202032347-ba08123207a945a0
Test for Jeff Bailey.

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-2009 Michael Foord, Nicola Larosa
 
3
# Copyright (C) 2005 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
9
8
 
10
9
# Released subject to the BSD License
11
 
# Please see http://www.voidspace.org.uk/python/license.shtml
 
10
# Please see http://www.voidspace.org.uk/documents/BSD-LICENSE.txt
12
11
 
13
12
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
14
13
# For information about bugfixes, updates and support, please join the
16
15
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
17
16
# Comments, suggestions and bug reports welcome.
18
17
 
19
 
 
20
 
from __future__ import generators
 
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
"""
21
32
 
22
33
import sys
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 $'
 
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 $'
123
47
 
124
48
__docformat__ = "restructuredtext en"
125
49
 
126
50
__all__ = (
127
51
    '__version__',
 
52
    'BOM_UTF8',
128
53
    'DEFAULT_INDENT_TYPE',
129
 
    'DEFAULT_INTERPOLATION',
 
54
    'NUM_INDENT_SPACES',
 
55
    'MAX_INTERPOL_DEPTH',
130
56
    'ConfigObjError',
131
57
    'NestingError',
132
58
    'ParseError',
135
61
    'ConfigObj',
136
62
    'SimpleVal',
137
63
    'InterpolationError',
138
 
    'InterpolationLoopError',
 
64
    'InterpolationDepthError',
139
65
    'MissingInterpolationOption',
140
66
    'RepeatSectionError',
141
 
    'ReloadError',
142
 
    'UnreprError',
143
 
    'UnknownType',
144
67
    '__docformat__',
145
 
    'flatten_errors',
146
68
)
147
69
 
148
 
DEFAULT_INTERPOLATION = 'configparser'
149
 
DEFAULT_INDENT_TYPE = '    '
 
70
DEFAULT_INDENT_TYPE = ' '
 
71
NUM_INDENT_SPACES = 4
150
72
MAX_INTERPOL_DEPTH = 10
151
73
 
152
74
OPTION_DEFAULTS = {
159
81
    'stringify': True,
160
82
    # option may be set to one of ('', ' ', '\t')
161
83
    'indent_type': None,
162
 
    'encoding': None,
163
 
    'default_encoding': None,
164
 
    'unrepr': False,
165
 
    'write_empty_values': False,
166
84
}
167
85
 
168
 
 
169
 
 
170
 
def getObj(s):
171
 
    s = "a=" + s
172
 
    if compiler is None:
173
 
        raise ImportError('compiler module not available')
174
 
    p = compiler.parse(s)
175
 
    return p.getChildren()[1].getChildren()[0].getChildren()[1]
176
 
 
177
 
 
178
 
class UnknownType(Exception):
179
 
    pass
180
 
 
181
 
 
182
 
class Builder(object):
183
 
 
184
 
    def build(self, o):
185
 
        m = getattr(self, 'build_' + o.__class__.__name__, None)
186
 
        if m is None:
187
 
            raise UnknownType(o.__class__.__name__)
188
 
        return m(o)
189
 
 
190
 
    def build_List(self, o):
191
 
        return map(self.build, o.getChildren())
192
 
 
193
 
    def build_Const(self, o):
194
 
        return o.value
195
 
 
196
 
    def build_Dict(self, o):
197
 
        d = {}
198
 
        i = iter(map(self.build, o.getChildren()))
199
 
        for el in i:
200
 
            d[el] = i.next()
201
 
        return d
202
 
 
203
 
    def build_Tuple(self, o):
204
 
        return tuple(self.build_List(o))
205
 
 
206
 
    def build_Name(self, o):
207
 
        if o.name == 'None':
208
 
            return None
209
 
        if o.name == 'True':
210
 
            return True
211
 
        if o.name == 'False':
212
 
            return False
213
 
 
214
 
        # An undefined Name
215
 
        raise UnknownType('Undefined Name')
216
 
 
217
 
    def build_Add(self, o):
218
 
        real, imag = map(self.build_Const, o.getChildren())
219
 
        try:
220
 
            real = float(real)
221
 
        except TypeError:
222
 
            raise UnknownType('Add')
223
 
        if not isinstance(imag, complex) or imag.real != 0.0:
224
 
            raise UnknownType('Add')
225
 
        return real+imag
226
 
 
227
 
    def build_Getattr(self, o):
228
 
        parent = self.build(o.expr)
229
 
        return getattr(parent, o.attrname)
230
 
 
231
 
    def build_UnarySub(self, o):
232
 
        return -self.build_Const(o.getChildren()[0])
233
 
 
234
 
    def build_UnaryAdd(self, o):
235
 
        return self.build_Const(o.getChildren()[0])
236
 
 
237
 
 
238
 
_builder = Builder()
239
 
 
240
 
 
241
 
def unrepr(s):
242
 
    if not s:
243
 
        return s
244
 
    return _builder.build(getObj(s))
245
 
 
246
 
 
247
 
 
248
86
class ConfigObjError(SyntaxError):
249
87
    """
250
88
    This is the base class for all errors that ConfigObj raises.
251
89
    It is a subclass of SyntaxError.
 
90
    
 
91
    >>> raise ConfigObjError
 
92
    Traceback (most recent call last):
 
93
    ConfigObjError
252
94
    """
253
95
    def __init__(self, message='', line_number=None, line=''):
254
96
        self.line = line
255
97
        self.line_number = line_number
 
98
        self.message = message
256
99
        SyntaxError.__init__(self, message)
257
100
 
258
 
 
259
101
class NestingError(ConfigObjError):
260
102
    """
261
103
    This error indicates a level of nesting that doesn't match.
 
104
    
 
105
    >>> raise NestingError
 
106
    Traceback (most recent call last):
 
107
    NestingError
262
108
    """
263
109
 
264
 
 
265
110
class ParseError(ConfigObjError):
266
111
    """
267
112
    This error indicates that a line is badly written.
268
113
    It is neither a valid ``key = value`` line,
269
114
    nor a valid section marker line.
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
 
 
 
115
    
 
116
    >>> raise ParseError
 
117
    Traceback (most recent call last):
 
118
    ParseError
 
119
    """
281
120
 
282
121
class DuplicateError(ConfigObjError):
283
122
    """
284
123
    The keyword or section specified already exists.
 
124
    
 
125
    >>> raise DuplicateError
 
126
    Traceback (most recent call last):
 
127
    DuplicateError
285
128
    """
286
129
 
287
 
 
288
130
class ConfigspecError(ConfigObjError):
289
131
    """
290
132
    An error occured whilst parsing a configspec.
 
133
    
 
134
    >>> raise ConfigspecError
 
135
    Traceback (most recent call last):
 
136
    ConfigspecError
291
137
    """
292
138
 
293
 
 
294
139
class InterpolationError(ConfigObjError):
295
140
    """Base class for the two interpolation errors."""
296
141
 
297
 
 
298
 
class InterpolationLoopError(InterpolationError):
 
142
class InterpolationDepthError(InterpolationError):
299
143
    """Maximum interpolation depth exceeded in string interpolation."""
300
144
 
301
145
    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
        """
302
151
        InterpolationError.__init__(
303
152
            self,
304
 
            'interpolation loop detected in value "%s".' % option)
305
 
 
 
153
            'max interpolation depth exceeded in value "%s".' % option)
306
154
 
307
155
class RepeatSectionError(ConfigObjError):
308
156
    """
309
157
    This error indicates additional sections in a section with a
310
158
    ``__many__`` (repeated) section.
 
159
    
 
160
    >>> raise RepeatSectionError
 
161
    Traceback (most recent call last):
 
162
    RepeatSectionError
311
163
    """
312
164
 
313
 
 
314
165
class MissingInterpolationOption(InterpolationError):
315
166
    """A value specified for interpolation was missing."""
316
167
 
317
168
    def __init__(self, option):
 
169
        """
 
170
        >>> raise MissingInterpolationOption('yoda')
 
171
        Traceback (most recent call last):
 
172
        MissingInterpolationOption: missing option "yoda" in interpolation.
 
173
        """
318
174
        InterpolationError.__init__(
319
175
            self,
320
176
            'missing option "%s" in interpolation.' % option)
321
177
 
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
 
 
493
178
class Section(dict):
494
179
    """
495
180
    A dictionary-like object that represents a section in a config file.
496
 
 
497
 
    It does string interpolation if the 'interpolation' attribute
 
181
    
 
182
    It does string interpolation if the 'interpolate' attribute
498
183
    of the 'main' object is set to True.
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
 
 
 
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
    
504
188
    A Section will behave like an ordered dictionary - following the
505
189
    order of the ``scalars`` and ``sections`` attributes.
506
190
    You can use this to change the order of members.
507
 
 
 
191
    
508
192
    Iteration follows the order: scalars, then sections.
509
193
    """
510
194
 
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
 
 
 
195
    _KEYCRE = re.compile(r"%\(([^)]*)\)s|.")
520
196
 
521
197
    def __init__(self, parent, depth, main, indict=None, name=None):
522
198
        """
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
 
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
527
203
        """
528
204
        if indict is None:
529
205
            indict = {}
534
210
        self.main = main
535
211
        # level of nesting depth of this Section
536
212
        self.depth = depth
 
213
        # the sequence of scalar values in this Section
 
214
        self.scalars = []
 
215
        # the sequence of sections in this Section
 
216
        self.sections = []
537
217
        # purely for information
538
218
        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):
548
 
        # the sequence of scalar values in this Section
549
 
        self.scalars = []
550
 
        # the sequence of sections in this Section
551
 
        self.sections = []
552
219
        # for comments :-)
553
220
        self.comments = {}
554
221
        self.inline_comments = {}
555
 
        # the configspec
556
 
        self.configspec = None
 
222
        # for the configspec
 
223
        self.configspec = {}
557
224
        # for defaults
558
225
        self.defaults = []
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
 
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)
578
240
            else:
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)
 
241
                break
 
242
        else:
 
243
            raise InterpolationDepthError(value)
 
244
        return value
583
245
 
 
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
584
266
 
585
267
    def __getitem__(self, key):
586
268
        """Fetch the item and do string interpolation."""
587
269
        val = dict.__getitem__(self, key)
588
 
        if self.main.interpolation and isinstance(val, basestring):
589
 
            return self._interpolate(key, val)
 
270
        if self.main.interpolation and isinstance(val, StringTypes):
 
271
            return self._interpolate(val)
590
272
        return val
591
273
 
592
 
 
593
 
    def __setitem__(self, key, value, unrepr=False):
 
274
    def __setitem__(self, key, value):
594
275
        """
595
276
        Correctly set a value.
596
 
 
 
277
        
597
278
        Making dictionary values Section instances.
598
279
        (We have to special case 'Section' instances - which are also dicts)
599
 
 
 
280
        
600
281
        Keys must be strings.
601
282
        Values need only be strings (or lists of strings) if
602
283
        ``main.stringify`` is set.
603
 
 
604
 
        ``unrepr`` must be set when setting a value to a dictionary, without
605
 
        creating a new sub-section.
606
284
        """
607
 
        if not isinstance(key, basestring):
608
 
            raise ValueError('The key "%s" is not a string.' % key)
609
 
 
 
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
610
289
        # add the comment
611
 
        if key not in self.comments:
 
290
        if not self.comments.has_key(key):
612
291
            self.comments[key] = []
613
292
            self.inline_comments[key] = ''
614
293
        # remove the entry from defaults
616
295
            self.defaults.remove(key)
617
296
        #
618
297
        if isinstance(value, Section):
619
 
            if key not in self:
 
298
            if not self.has_key(key):
620
299
                self.sections.append(key)
621
300
            dict.__setitem__(self, key, value)
622
 
        elif isinstance(value, dict) and not unrepr:
 
301
        elif isinstance(value, dict):
623
302
            # First create the new depth level,
624
303
            # then create the section
625
 
            if key not in self:
 
304
            if not self.has_key(key):
626
305
                self.sections.append(key)
627
306
            new_depth = self.depth + 1
628
307
            dict.__setitem__(
635
314
                    indict=value,
636
315
                    name=key))
637
316
        else:
638
 
            if key not in self:
 
317
            if not self.has_key(key):
639
318
                self.scalars.append(key)
640
319
            if not self.main.stringify:
641
 
                if isinstance(value, basestring):
 
320
                if isinstance(value, StringTypes):
642
321
                    pass
643
322
                elif isinstance(value, (list, tuple)):
644
323
                    for entry in value:
645
 
                        if not isinstance(entry, basestring):
646
 
                            raise TypeError('Value is not a string "%s".' % entry)
 
324
                        if not isinstance(entry, StringTypes):
 
325
                            raise TypeError, (
 
326
                                'Value is not a string "%s".' % entry)
647
327
                else:
648
 
                    raise TypeError('Value is not a string "%s".' % value)
 
328
                    raise TypeError, 'Value is not a string "%s".' % value
649
329
            dict.__setitem__(self, key, value)
650
330
 
651
 
 
652
331
    def __delitem__(self, key):
653
332
        """Remove items from the sequence when deleting."""
654
333
        dict. __delitem__(self, key)
659
338
        del self.comments[key]
660
339
        del self.inline_comments[key]
661
340
 
662
 
 
663
341
    def get(self, key, default=None):
664
342
        """A version of ``get`` that doesn't bypass string interpolation."""
665
343
        try:
667
345
        except KeyError:
668
346
            return default
669
347
 
670
 
 
671
348
    def update(self, indict):
672
 
        """
673
 
        A version of update that uses our ``__setitem__``.
674
 
        """
 
349
        """A version of update that uses our ``__setitem__``."""
675
350
        for entry in indict:
676
351
            self[entry] = indict[entry]
677
352
 
678
 
 
679
353
    def pop(self, key, *args):
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
 
        """
 
354
        """ """
684
355
        val = dict.pop(self, key, *args)
685
356
        if key in self.scalars:
686
357
            del self.comments[key]
690
361
            del self.comments[key]
691
362
            del self.inline_comments[key]
692
363
            self.sections.remove(key)
693
 
        if self.main.interpolation and isinstance(val, basestring):
694
 
            return self._interpolate(key, val)
 
364
        if self.main.interpolation and isinstance(val, StringTypes):
 
365
            return self._interpolate(val)
695
366
        return val
696
367
 
697
 
 
698
368
    def popitem(self):
699
369
        """Pops the first (key,val)"""
700
370
        sequence = (self.scalars + self.sections)
701
371
        if not sequence:
702
 
            raise KeyError(": 'popitem(): dictionary is empty'")
 
372
            raise KeyError, ": 'popitem(): dictionary is empty'"
703
373
        key = sequence[0]
704
374
        val =  self[key]
705
375
        del self[key]
706
376
        return key, val
707
377
 
708
 
 
709
378
    def clear(self):
710
379
        """
711
380
        A version of clear that also affects scalars/sections
712
381
        Also clears comments and configspec.
713
 
 
 
382
        
714
383
        Leaves other attributes alone :
715
384
            depth/main/parent are not affected
716
385
        """
719
388
        self.sections = []
720
389
        self.comments = {}
721
390
        self.inline_comments = {}
722
 
        self.configspec = None
723
 
 
 
391
        self.configspec = {}
724
392
 
725
393
    def setdefault(self, key, default=None):
726
394
        """A version of setdefault that sets sequence if appropriate."""
730
398
            self[key] = default
731
399
            return self[key]
732
400
 
733
 
 
734
401
    def items(self):
735
 
        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
 
402
        """ """
736
403
        return zip((self.scalars + self.sections), self.values())
737
404
 
738
 
 
739
405
    def keys(self):
740
 
        """D.keys() -> list of D's keys"""
 
406
        """ """
741
407
        return (self.scalars + self.sections)
742
408
 
743
 
 
744
409
    def values(self):
745
 
        """D.values() -> list of D's values"""
 
410
        """ """
746
411
        return [self[key] for key in (self.scalars + self.sections)]
747
412
 
748
 
 
749
413
    def iteritems(self):
750
 
        """D.iteritems() -> an iterator over the (key, value) items of D"""
 
414
        """ """
751
415
        return iter(self.items())
752
416
 
753
 
 
754
417
    def iterkeys(self):
755
 
        """D.iterkeys() -> an iterator over the keys of D"""
 
418
        """ """
756
419
        return iter((self.scalars + self.sections))
757
420
 
758
421
    __iter__ = iterkeys
759
422
 
760
 
 
761
423
    def itervalues(self):
762
 
        """D.itervalues() -> an iterator over the values of D"""
 
424
        """ """
763
425
        return iter(self.values())
764
426
 
765
 
 
766
427
    def __repr__(self):
767
 
        """x.__repr__() <==> repr(x)"""
768
428
        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
769
429
            for key in (self.scalars + self.sections)])
770
430
 
771
431
    __str__ = __repr__
772
 
    __str__.__doc__ = "x.__str__() <==> str(x)"
773
 
 
774
432
 
775
433
    # Extra methods - not in a normal dictionary
776
434
 
777
435
    def dict(self):
778
436
        """
779
437
        Return a deepcopy of self as a dictionary.
780
 
 
 
438
        
781
439
        All members that are ``Section`` instances are recursively turned to
782
440
        ordinary dictionaries - by calling their ``dict`` method.
783
 
 
 
441
        
784
442
        >>> n = a.dict()
785
443
        >>> n == a
786
444
        1
792
450
            this_entry = self[entry]
793
451
            if isinstance(this_entry, Section):
794
452
                this_entry = this_entry.dict()
795
 
            elif isinstance(this_entry, list):
 
453
            elif isinstance(this_entry, (list, tuple)):
796
454
                # create a copy rather than a reference
797
455
                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)
801
456
            newdict[entry] = this_entry
802
457
        return newdict
803
458
 
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
 
 
832
459
    def rename(self, oldkey, newkey):
833
460
        """
834
461
        Change a keyname to another, without changing position in sequence.
835
 
 
 
462
        
836
463
        Implemented so that transformations can be made on keys,
837
464
        as well as on values. (used by encode and decode)
838
 
 
 
465
        
839
466
        Also renames comments.
840
467
        """
841
468
        if oldkey in self.scalars:
843
470
        elif oldkey in self.sections:
844
471
            the_list = self.sections
845
472
        else:
846
 
            raise KeyError('Key "%s" not found.' % oldkey)
 
473
            raise KeyError, 'Key "%s" not found.' % oldkey
847
474
        pos = the_list.index(oldkey)
848
475
        #
849
476
        val = self[oldkey]
858
485
        self.comments[newkey] = comm
859
486
        self.inline_comments[newkey] = inline_comment
860
487
 
861
 
 
862
488
    def walk(self, function, raise_errors=True,
863
489
            call_on_sections=False, **keywargs):
864
490
        """
865
491
        Walk every member and call a function on the keyword and value.
866
 
 
 
492
        
867
493
        Return a dictionary of the return values
868
 
 
 
494
        
869
495
        If the function raises an exception, raise the errror
870
496
        unless ``raise_errors=False``, in which case set the return value to
871
497
        ``False``.
872
 
 
 
498
        
873
499
        Any unrecognised keyword arguments you pass to walk, will be pased on
874
500
        to the function you pass in.
875
 
 
 
501
        
876
502
        Note: if ``call_on_sections`` is ``True`` then - on encountering a
877
503
        subsection, *first* the function is called for the *whole* subsection,
878
504
        and then recurses into it's members. This means your function must be
879
505
        able to handle strings, dictionaries and lists. This allows you
880
506
        to change the key of subsections as well as for ordinary members. The
881
507
        return value when called on the whole subsection has to be discarded.
882
 
 
 
508
        
883
509
        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'}})
908
510
        """
909
511
        out = {}
910
512
        # scalars first
911
 
        for i in range(len(self.scalars)):
912
 
            entry = self.scalars[i]
 
513
        for entry in self.scalars[:]:
913
514
            try:
914
 
                val = function(self, entry, **keywargs)
915
 
                # bound again in case name has changed
916
 
                entry = self.scalars[i]
917
 
                out[entry] = val
 
515
                out[entry] = function(self, entry, **keywargs)
918
516
            except Exception:
919
517
                if raise_errors:
920
518
                    raise
921
519
                else:
922
 
                    entry = self.scalars[i]
923
520
                    out[entry] = False
924
521
        # then sections
925
 
        for i in range(len(self.sections)):
926
 
            entry = self.sections[i]
 
522
        for entry in self.sections[:]:
927
523
            if call_on_sections:
928
524
                try:
929
525
                    function(self, entry, **keywargs)
931
527
                    if raise_errors:
932
528
                        raise
933
529
                    else:
934
 
                        entry = self.sections[i]
935
530
                        out[entry] = False
936
 
                # bound again in case name has changed
937
 
                entry = self.sections[i]
938
531
            # previous result is discarded
939
532
            out[entry] = self[entry].walk(
940
533
                function,
943
536
                **keywargs)
944
537
        return out
945
538
 
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
 
 
 
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)
1090
604
 
1091
605
class ConfigObj(Section):
1092
 
    """An object to read, create, and write config files."""
 
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
    """
1093
637
 
1094
638
    _keyword = re.compile(r'''^ # line start
1095
639
        (\s*)                   # indentation
1119
663
 
1120
664
    # this regexp pulls list values out as a single string
1121
665
    # or single values and comments
1122
 
    # FIXME: this regex adds a '' to the end of comma terminated lists
1123
 
    #   workaround in ``_handle_value``
1124
666
    _valueexp = re.compile(r'''^
1125
667
        (?:
1126
668
            (?:
1129
671
                        (?:
1130
672
                            (?:".*?")|              # double quotes
1131
673
                            (?:'.*?')|              # single quotes
1132
 
                            (?:[^'",\#][^,\#]*?)    # unquoted
 
674
                            (?:[^'",\#][^,\#]*?)       # unquoted
1133
675
                        )
1134
676
                        \s*,\s*                     # comma
1135
677
                    )*      # match all list items ending in a comma (if any)
1137
679
                (
1138
680
                    (?:".*?")|                      # double quotes
1139
681
                    (?:'.*?')|                      # single quotes
1140
 
                    (?:[^'",\#\s][^,]*?)|           # unquoted
1141
 
                    (?:(?<!,))                      # Empty value
 
682
                    (?:[^'",\#\s][^,]*?)             # unquoted
1142
683
                )?          # last item in a list - or string value
1143
684
            )|
1144
685
            (,)             # alternatively a single comma - empty list
1164
705
        (
1165
706
            (?:".*?")|          # double quotes
1166
707
            (?:'.*?')|          # single quotes
1167
 
            (?:[^'"\#].*?)|     # unquoted
1168
 
            (?:)                # Empty value
 
708
            (?:[^'"\#].*?)      # unquoted
1169
709
        )
1170
710
        \s*(\#.*)?              # optional comment
1171
711
        $''',
1182
722
        '"""': (_single_line_double, _multi_line_double),
1183
723
    }
1184
724
 
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):
 
725
    def __init__(self, infile=None, options=None, **kwargs):
1195
726
        """
1196
 
        Parse a config file or create a config file object.
1197
 
 
 
727
        Parse or create a config file object.
 
728
        
1198
729
        ``ConfigObj(infile=None, options=None, **kwargs)``
1199
730
        """
1200
 
        self._inspec = _inspec
 
731
        if infile is None:
 
732
            infile = []
 
733
        if options is None:
 
734
            options = {}
 
735
        # keyword arguments take precedence over an options dictionary
 
736
        options.update(kwargs)
1201
737
        # init the superclass
1202
738
        Section.__init__(self, self, 0, self)
1203
 
 
1204
 
        infile = infile or []
1205
 
        options = dict(options or {})
1206
 
 
1207
 
        # keyword arguments take precedence over an options dictionary
1208
 
        options.update(kwargs)
1209
 
        if _inspec:
1210
 
            options['list_values'] = False
1211
 
 
 
739
        #
1212
740
        defaults = OPTION_DEFAULTS.copy()
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
 
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
1219
746
        defaults.update(options)
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()
 
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()
1233
767
            elif self.file_error:
1234
768
                # raise an error if the file doesn't exist
1235
 
                raise IOError('Config file not found: "%s".' % self.filename)
 
769
                raise IOError, 'Config file not found: "%s".' % self.filename
1236
770
            else:
1237
771
                # file doesn't already exist
1238
772
                if self.create_empty:
1239
773
                    # this is a good test that the filename specified
1240
 
                    # isn't impossible - like on a non-existent device
1241
 
                    h = open(infile, 'w')
 
774
                    # isn't impossible - like on a non existent device
 
775
                    h = open(self.filename)
1242
776
                    h.write('')
1243
777
                    h.close()
1244
778
                infile = []
1245
 
 
1246
779
        elif isinstance(infile, (list, tuple)):
1247
 
            infile = list(infile)
1248
 
 
 
780
            self.filename = None
1249
781
        elif isinstance(infile, dict):
1250
782
            # initialise self
1251
783
            # the Section class handles creating subsections
1252
784
            if isinstance(infile, ConfigObj):
1253
785
                # get a copy of our ConfigObj
1254
786
                infile = infile.dict()
1255
 
 
1256
787
            for entry in infile:
1257
788
                self[entry] = infile[entry]
 
789
            self.filename = None
1258
790
            del self._errors
1259
 
 
1260
 
            if configspec is not None:
1261
 
                self._handle_configspec(configspec)
 
791
            if defaults['configspec'] is not None:
 
792
                self._handle_configspec(defaults['configspec'])
1262
793
            else:
1263
794
                self.configspec = None
1264
795
            return
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
 
 
 
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
        #
1292
817
        self._parse(infile)
1293
818
        # if we had any errors, now is the time to raise them
1294
819
        if self._errors:
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]
 
820
            error = ConfigObjError("Parsing failed.")
1301
821
            # set the errors attribute; it's a list of tuples:
1302
822
            # (error_type, message, line_number)
1303
823
            error.errors = self._errors
1306
826
            raise error
1307
827
        # delete private attributes
1308
828
        del self._errors
1309
 
 
1310
 
        if configspec is None:
 
829
        #
 
830
        if defaults['configspec'] is None:
1311
831
            self.configspec = None
1312
832
        else:
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
 
 
 
833
            self._handle_configspec(defaults['configspec'])
1501
834
 
1502
835
    def _parse(self, infile):
1503
 
        """Actually parse the config file."""
1504
 
        temp_list_values = self.list_values
1505
 
        if self.unrepr:
1506
 
            self.list_values = False
1507
 
 
 
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
        """
1508
906
        comment_list = []
1509
907
        done_start = False
1510
908
        this_section = self
1511
909
        maxline = len(infile) - 1
1512
910
        cur_index = -1
1513
911
        reset_comment = False
1514
 
 
1515
912
        while cur_index < maxline:
1516
913
            if reset_comment:
1517
914
                comment_list = []
1523
920
                reset_comment = False
1524
921
                comment_list.append(line)
1525
922
                continue
1526
 
 
1527
923
            if not done_start:
1528
924
                # preserve initial comment
1529
925
                self.initial_comment = comment_list
1530
926
                comment_list = []
1531
927
                done_start = True
1532
 
 
1533
928
            reset_comment = True
1534
929
            # first we check if it's a section marker
1535
930
            mat = self._sectionmarker.match(line)
 
931
##            print >> sys.stderr, sline, mat
1536
932
            if mat is not None:
1537
933
                # is a section line
1538
 
                (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
 
934
                (indent, sect_open, sect_name, sect_close, comment) = (
 
935
                    mat.groups())
1539
936
                if indent and (self.indent_type is None):
1540
 
                    self.indent_type = indent
 
937
                    self.indent_type = indent[0]
1541
938
                cur_depth = sect_open.count('[')
1542
939
                if cur_depth != sect_close.count(']'):
1543
 
                    self._handle_error("Cannot compute the section depth at line %s.",
1544
 
                                       NestingError, infile, cur_index)
 
940
                    self._handle_error(
 
941
                        "Cannot compute the section depth at line %s.",
 
942
                        NestingError, infile, cur_index)
1545
943
                    continue
1546
 
 
1547
944
                if cur_depth < this_section.depth:
1548
945
                    # the new section is dropping back to a previous level
1549
946
                    try:
1550
 
                        parent = self._match_depth(this_section,
1551
 
                                                   cur_depth).parent
 
947
                        parent = self._match_depth(
 
948
                            this_section,
 
949
                            cur_depth).parent
1552
950
                    except SyntaxError:
1553
 
                        self._handle_error("Cannot compute nesting level at line %s.",
1554
 
                                           NestingError, infile, cur_index)
 
951
                        self._handle_error(
 
952
                            "Cannot compute nesting level at line %s.",
 
953
                            NestingError, infile, cur_index)
1555
954
                        continue
1556
955
                elif cur_depth == this_section.depth:
1557
956
                    # the new section is a sibling of the current section
1560
959
                    # the new section is a child the current section
1561
960
                    parent = this_section
1562
961
                else:
1563
 
                    self._handle_error("Section too nested at line %s.",
1564
 
                                       NestingError, infile, cur_index)
1565
 
 
 
962
                    self._handle_error(
 
963
                        "Section too nested at line %s.",
 
964
                        NestingError, infile, cur_index)
 
965
                #
1566
966
                sect_name = self._unquote(sect_name)
1567
 
                if sect_name in parent:
1568
 
                    self._handle_error('Duplicate section name at line %s.',
1569
 
                                       DuplicateError, infile, cur_index)
 
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)
1570
972
                    continue
1571
 
 
1572
973
                # create the new section
1573
974
                this_section = Section(
1574
975
                    parent,
1578
979
                parent[sect_name] = this_section
1579
980
                parent.inline_comments[sect_name] = comment
1580
981
                parent.comments[sect_name] = comment_list
 
982
##                print >> sys.stderr, parent[sect_name] is this_section
1581
983
                continue
1582
984
            #
1583
985
            # it's not a section marker,
1584
986
            # so it should be a valid ``key = value`` line
1585
987
            mat = self._keyword.match(line)
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:
 
988
##            print >> sys.stderr, sline, mat
 
989
            if mat is not None:
1593
990
                # is a keyword value
1594
991
                # value will include any inline comment
1595
992
                (indent, key, value) = mat.groups()
1596
993
                if indent and (self.indent_type is None):
1597
 
                    self.indent_type = indent
 
994
                    self.indent_type = indent[0]
1598
995
                # check for a multiline value
1599
996
                if value[:3] in ['"""', "'''"]:
1600
997
                    try:
1605
1002
                            'Parse error in value at line %s.',
1606
1003
                            ParseError, infile, cur_index)
1607
1004
                        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
1621
1005
                else:
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
 
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
1643
1014
                #
 
1015
##                print >> sys.stderr, sline
1644
1016
                key = self._unquote(key)
1645
 
                if key in this_section:
 
1017
                if this_section.has_key(key):
1646
1018
                    self._handle_error(
1647
1019
                        'Duplicate keyword name at line %s.',
1648
1020
                        DuplicateError, infile, cur_index)
1649
1021
                    continue
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)
 
1022
                # add the key
 
1023
##                print >> sys.stderr, this_section.name
 
1024
                this_section[key] = value
1654
1025
                this_section.inline_comments[key] = comment
1655
1026
                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]
1656
1032
                continue
1657
 
        #
 
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)
1658
1039
        if self.indent_type is None:
1659
1040
            # no indentation used, set the type accordingly
1660
1041
            self.indent_type = ''
1661
 
 
1662
1042
        # preserve the final comment
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
 
 
 
1043
        self.final_comment = comment_list
1669
1044
 
1670
1045
    def _match_depth(self, sect, depth):
1671
1046
        """
1672
1047
        Given a section and a depth level, walk back through the sections
1673
1048
        parents to see if the depth level matches a previous section.
1674
 
 
 
1049
        
1675
1050
        Return a reference to the right section,
1676
1051
        or raise a SyntaxError.
1677
1052
        """
1678
1053
        while depth < sect.depth:
1679
1054
            if sect is sect.parent:
1680
1055
                # we've reached the top level already
1681
 
                raise SyntaxError()
 
1056
                raise SyntaxError
1682
1057
            sect = sect.parent
1683
1058
        if sect.depth == depth:
1684
1059
            return sect
1685
1060
        # shouldn't get here
1686
 
        raise SyntaxError()
1687
 
 
 
1061
        raise SyntaxError
1688
1062
 
1689
1063
    def _handle_error(self, text, ErrorClass, infile, cur_index):
1690
1064
        """
1691
1065
        Handle an error according to the error settings.
1692
 
 
 
1066
        
1693
1067
        Either raise the error or store it.
1694
1068
        The error will have occured at ``cur_index``
1695
1069
        """
1696
1070
        line = infile[cur_index]
1697
 
        cur_index += 1
1698
1071
        message = text % cur_index
1699
1072
        error = ErrorClass(message, cur_index, line)
1700
1073
        if self.raise_errors:
1704
1077
        # reraise when parsing has finished
1705
1078
        self._errors.append(error)
1706
1079
 
1707
 
 
1708
1080
    def _unquote(self, value):
1709
1081
        """Return an unquoted version of a value"""
1710
1082
        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1711
1083
            value = value[1:-1]
1712
1084
        return value
1713
1085
 
1714
 
 
1715
1086
    def _quote(self, value, multiline=True):
1716
1087
        """
1717
1088
        Return a safely quoted version of a value.
1718
 
 
 
1089
        
1719
1090
        Raise a ConfigObjError if the value cannot be safely quoted.
1720
1091
        If multiline is ``True`` (default) then use triple quotes
1721
1092
        if necessary.
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.
 
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.
1733
1098
        """
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)):
 
1099
        if isinstance(value, (list, tuple)):
1740
1100
            if not value:
1741
1101
                return ','
1742
1102
            elif len(value) == 1:
1743
1103
                return self._quote(value[0], multiline=False) + ','
1744
 
            return ', '.join([self._quote(val, multiline=False)
 
1104
            return ','.join([self._quote(val, multiline=False)
1745
1105
                for val in value])
1746
 
        if not isinstance(value, basestring):
 
1106
        if not isinstance(value, StringTypes):
1747
1107
            if self.stringify:
1748
1108
                value = str(value)
1749
1109
            else:
1750
 
                raise TypeError('Value "%s" is not a string.' % value)
1751
 
 
 
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'''"
1752
1117
        if not value:
1753
1118
            return '""'
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
 
1119
        if not (multiline and
 
1120
                ((("'" in value) and ('"' in value)) or ('\n' in value))):
1764
1121
            # for normal values either single or double quotes will do
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
 
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
1769
1126
                    (value[-1] not in wspace_plus) and
1770
1127
                    (',' not in value)):
1771
1128
                quot = noquot
1772
1129
            else:
1773
 
                quot = self._get_single_quote(value)
 
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
1774
1137
        else:
1775
1138
            # if value has '\n' or "'" *and* '"', it will need triple quotes
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
 
 
 
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
1781
1146
        return quot % value
1782
1147
 
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
 
        if value.find('"""') == -1:
1798
 
            quot = tdquot
1799
 
        else:
1800
 
            quot = tsquot
1801
 
        return quot
1802
 
 
1803
 
 
1804
1148
    def _handle_value(self, value):
1805
1149
        """
1806
1150
        Given a value string, unquote, remove comment,
1807
1151
        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
1808
1240
        """
1809
 
        if self._inspec:
1810
 
            # Parsing a configspec so don't handle comments
1811
 
            return (value, '')
1812
1241
        # do we look for lists in values ?
1813
1242
        if not self.list_values:
1814
1243
            mat = self._nolistvalue.match(value)
1815
1244
            if mat is None:
1816
 
                raise SyntaxError()
1817
 
            # NOTE: we don't unquote here
1818
 
            return mat.groups()
1819
 
        #
 
1245
                raise SyntaxError
 
1246
            (value, comment) = mat.groups()
 
1247
            # FIXME: unquoting here can be a source of error
 
1248
            return (self._unquote(value), comment)
1820
1249
        mat = self._valueexp.match(value)
1821
1250
        if mat is None:
1822
1251
            # the value is badly constructed, probably badly quoted,
1823
1252
            # or an invalid list
1824
 
            raise SyntaxError()
 
1253
            raise SyntaxError
1825
1254
        (list_values, single, empty_list, comment) = mat.groups()
1826
1255
        if (list_values == '') and (single is None):
1827
1256
            # change this if you want to accept empty values
1828
 
            raise SyntaxError()
 
1257
            raise SyntaxError
1829
1258
        # NOTE: note there is no error handling from here if the regex
1830
1259
        # is wrong: then incorrect values will slip through
1831
1260
        if empty_list is not None:
1832
1261
            # the single comma - meaning an empty list
1833
1262
            return ([], comment)
1834
1263
        if single is not None:
1835
 
            # handle empty values
1836
 
            if list_values and not single:
1837
 
                # FIXME: the '' is a workaround because our regex now matches
1838
 
                #   '' at the end of a list if it has a trailing comma
1839
 
                single = None
1840
 
            else:
1841
 
                single = single or '""'
1842
 
                single = self._unquote(single)
 
1264
            single = self._unquote(single)
1843
1265
        if list_values == '':
1844
1266
            # not a list value
1845
1267
            return (single, comment)
1849
1271
            the_list += [single]
1850
1272
        return (the_list, comment)
1851
1273
 
1852
 
 
1853
1274
    def _multiline(self, value, infile, cur_index, maxline):
1854
 
        """Extract the value, where we are in a multiline situation."""
 
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
        """
1855
1298
        quot = value[:3]
1856
1299
        newvalue = value[3:]
1857
1300
        single_line = self._triple_quote[quot][0]
1863
1306
            return retval
1864
1307
        elif newvalue.find(quot) != -1:
1865
1308
            # somehow the triple quote is missing
1866
 
            raise SyntaxError()
 
1309
            raise SyntaxError
1867
1310
        #
1868
1311
        while cur_index < maxline:
1869
1312
            cur_index += 1
1876
1319
                break
1877
1320
        else:
1878
1321
            # we've got to the end of the config, oops...
1879
 
            raise SyntaxError()
 
1322
            raise SyntaxError
1880
1323
        mat = multi_line.match(line)
1881
1324
        if mat is None:
1882
1325
            # a badly formed line
1883
 
            raise SyntaxError()
 
1326
            raise SyntaxError
1884
1327
        (value, comment) = mat.groups()
1885
1328
        return (newvalue + value, comment, cur_index)
1886
1329
 
1887
 
 
1888
1330
    def _handle_configspec(self, configspec):
1889
1331
        """Parse the configspec."""
1890
 
        # FIXME: Should we check that the configspec was created with the
1891
 
        #        correct settings ? (i.e. ``list_values=False``)
1892
 
        if not isinstance(configspec, ConfigObj):
1893
 
            try:
1894
 
                configspec = ConfigObj(configspec,
1895
 
                                       raise_errors=True,
1896
 
                                       file_error=True,
1897
 
                                       _inspec=True)
1898
 
            except ConfigObjError, e:
1899
 
                # FIXME: Should these errors have a reference
1900
 
                #        to the already parsed ConfigObj ?
1901
 
                raise ConfigspecError('Parsing configspec failed: %s' % e)
1902
 
            except IOError, e:
1903
 
                raise IOError('Reading configspec failed: %s' % e)
1904
 
 
1905
 
        self.configspec = configspec
1906
 
 
1907
 
 
1908
 
 
1909
 
    def _set_configspec(self, section, copy):
1910
 
        """
1911
 
        Called by validate. Handles setting the configspec on subsections
1912
 
        including sections to be validated by __many__
1913
 
        """
1914
 
        configspec = section.configspec
1915
 
        many = configspec.get('__many__')
1916
 
        if isinstance(many, dict):
1917
 
            for entry in section.sections:
1918
 
                if entry not in configspec:
1919
 
                    section[entry].configspec = many
1920
 
 
 
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]
1921
1355
        for entry in configspec.sections:
1922
1356
            if entry == '__many__':
1923
1357
                continue
1924
 
            if entry not in section:
1925
 
                section[entry] = {}
1926
 
                if copy:
1927
 
                    # copy comments
1928
 
                    section.comments[entry] = configspec.comments.get(entry, [])
1929
 
                    section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1930
 
 
1931
 
            # Could be a scalar when we expect a section
1932
 
            if isinstance(section[entry], Section):
1933
 
                section[entry].configspec = configspec[entry]
1934
 
 
 
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])
1935
1392
 
1936
1393
    def _write_line(self, indent_string, entry, this_entry, comment):
1937
1394
        """Write an individual line, for the write method"""
1938
 
        # NOTE: the calls to self._quote here handles non-StringType values.
1939
 
        if not self.unrepr:
1940
 
            val = self._decode_element(self._quote(this_entry))
1941
 
        else:
1942
 
            val = repr(this_entry)
1943
 
        return '%s%s%s%s%s' % (indent_string,
1944
 
                               self._decode_element(self._quote(entry, multiline=False)),
1945
 
                               self._a_to_u(' = '),
1946
 
                               val,
1947
 
                               self._decode_element(comment))
1948
 
 
 
1395
        return '%s%s = %s%s' % (
 
1396
            indent_string,
 
1397
            self._quote(entry, multiline=False),
 
1398
            self._quote(this_entry),
 
1399
            comment)
1949
1400
 
1950
1401
    def _write_marker(self, indent_string, depth, entry, comment):
1951
1402
        """Write a section marker line"""
1952
 
        return '%s%s%s%s%s' % (indent_string,
1953
 
                               self._a_to_u('[' * depth),
1954
 
                               self._quote(self._decode_element(entry), multiline=False),
1955
 
                               self._a_to_u(']' * depth),
1956
 
                               self._decode_element(comment))
1957
 
 
 
1403
        return '%s%s%s%s%s' % (
 
1404
            indent_string,
 
1405
            '[' * depth,
 
1406
            self._quote(entry, multiline=False),
 
1407
            ']' * depth,
 
1408
            comment)
1958
1409
 
1959
1410
    def _handle_comment(self, comment):
1960
 
        """Deal with a 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
        """
1961
1437
        if not comment:
1962
1438
            return ''
1963
 
        start = self.indent_type
 
1439
        if self.indent_type == '\t':
 
1440
            start = '\t'
 
1441
        else:
 
1442
            start = ' ' * NUM_INDENT_SPACES
1964
1443
        if not comment.startswith('#'):
1965
 
            start += self._a_to_u(' # ')
 
1444
            start += '# '
1966
1445
        return (start + comment)
1967
1446
 
 
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
1968
1459
 
1969
1460
    # Public methods
1970
1461
 
1971
 
    def write(self, outfile=None, section=None):
 
1462
    def write(self, section=None):
1972
1463
        """
1973
1464
        Write the current ConfigObj as a file
1974
 
 
 
1465
        
1975
1466
        tekNico: FIXME: use StringIO instead of real files
1976
 
 
 
1467
        
1977
1468
        >>> filename = a.filename
1978
1469
        >>> a.filename = 'test.ini'
1979
1470
        >>> a.write()
1980
1471
        >>> a.filename = filename
1981
1472
        >>> a == ConfigObj('test.ini', raise_errors=True)
1982
1473
        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']
1983
1490
        """
 
1491
        int_val = 'test'
1984
1492
        if self.indent_type is None:
1985
1493
            # this can be true if initialised from a dictionary
1986
1494
            self.indent_type = DEFAULT_INDENT_TYPE
1987
 
 
 
1495
        #
1988
1496
        out = []
1989
 
        cs = self._a_to_u('#')
1990
 
        csp = self._a_to_u('# ')
 
1497
        return_list = True
1991
1498
        if section is None:
1992
1499
            int_val = self.interpolation
1993
1500
            self.interpolation = False
1994
1501
            section = self
 
1502
            return_list = False
1995
1503
            for line in self.initial_comment:
1996
 
                line = self._decode_element(line)
1997
1504
                stripped_line = line.strip()
1998
 
                if stripped_line and not stripped_line.startswith(cs):
1999
 
                    line = csp + line
 
1505
                if stripped_line and not stripped_line.startswith('#'):
 
1506
                    line = '# ' + line
2000
1507
                out.append(line)
2001
 
 
2002
 
        indent_string = self.indent_type * section.depth
 
1508
        #
 
1509
        indent_string = self._compute_indent_string(section.depth)
2003
1510
        for entry in (section.scalars + section.sections):
2004
1511
            if entry in section.defaults:
2005
1512
                # don't write out default values
2006
1513
                continue
2007
1514
            for comment_line in section.comments[entry]:
2008
 
                comment_line = self._decode_element(comment_line.lstrip())
2009
 
                if comment_line and not comment_line.startswith(cs):
2010
 
                    comment_line = csp + comment_line
 
1515
                comment_line = comment_line.lstrip()
 
1516
                if comment_line and not comment_line.startswith('#'):
 
1517
                    comment_line = '#' + comment_line
2011
1518
                out.append(indent_string + comment_line)
2012
1519
            this_entry = section[entry]
2013
1520
            comment = self._handle_comment(section.inline_comments[entry])
2014
 
 
 
1521
            #
2015
1522
            if isinstance(this_entry, dict):
2016
1523
                # a section
2017
1524
                out.append(self._write_marker(
2019
1526
                    this_entry.depth,
2020
1527
                    entry,
2021
1528
                    comment))
2022
 
                out.extend(self.write(section=this_entry))
 
1529
                out.extend(self.write(this_entry))
2023
1530
            else:
2024
1531
                out.append(self._write_line(
2025
1532
                    indent_string,
2026
1533
                    entry,
2027
1534
                    this_entry,
2028
1535
                    comment))
2029
 
 
2030
 
        if section is self:
 
1536
        #
 
1537
        if not return_list:
2031
1538
            for line in self.final_comment:
2032
 
                line = self._decode_element(line)
2033
1539
                stripped_line = line.strip()
2034
 
                if stripped_line and not stripped_line.startswith(cs):
2035
 
                    line = csp + line
 
1540
                if stripped_line and not stripped_line.startswith('#'):
 
1541
                    line = '# ' + line
2036
1542
                out.append(line)
 
1543
        #
 
1544
        if int_val != 'test':
2037
1545
            self.interpolation = int_val
2038
 
 
2039
 
        if section is not self:
2040
 
            return out
2041
 
 
2042
 
        if (self.filename is None) and (outfile is None):
2043
 
            # output a list of lines
2044
 
            # might need to encode
2045
 
            # NOTE: This will *screw* UTF16, each line will start with the BOM
2046
 
            if self.encoding:
2047
 
                out = [l.encode(self.encoding) for l in out]
2048
 
            if (self.BOM and ((self.encoding is None) or
2049
 
                (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2050
 
                # Add the UTF8 BOM
2051
 
                if not out:
2052
 
                    out.append('')
2053
 
                out[0] = BOM_UTF8 + out[0]
2054
 
            return out
2055
 
 
2056
 
        # Turn the list to a string, joined with correct newlines
2057
 
        newline = self.newlines or os.linesep
2058
 
        output = self._a_to_u(newline).join(out)
2059
 
        if self.encoding:
2060
 
            output = output.encode(self.encoding)
2061
 
        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2062
 
            # Add the UTF8 BOM
2063
 
            output = BOM_UTF8 + output
2064
 
 
2065
 
        if not output.endswith(newline):
2066
 
            output += newline
2067
 
        if outfile is not None:
2068
 
            outfile.write(output)
2069
 
        else:
2070
 
            h = open(self.filename, 'wb')
2071
 
            h.write(output)
 
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))
2072
1554
            h.close()
2073
 
 
2074
 
 
2075
 
    def validate(self, validator, preserve_errors=False, copy=False,
2076
 
                 section=None):
 
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):
2077
1563
        """
2078
1564
        Test the ConfigObj against a configspec.
2079
 
 
 
1565
        
2080
1566
        It uses the ``validator`` object from *validate.py*.
2081
 
 
 
1567
        
2082
1568
        To run ``validate`` on the current ConfigObj, call: ::
2083
 
 
 
1569
        
2084
1570
            test = config.validate(validator)
2085
 
 
 
1571
        
2086
1572
        (Normally having previously passed in the configspec when the ConfigObj
2087
1573
        was created - you can dynamically assign a dictionary of checks to the
2088
1574
        ``configspec`` attribute of a section though).
2089
 
 
 
1575
        
2090
1576
        It returns ``True`` if everything passes, or a dictionary of
2091
1577
        pass/fails (True/False). If every member of a subsection passes, it
2092
1578
        will just have the value ``True``. (It also returns ``False`` if all
2093
1579
        members fail).
2094
 
 
 
1580
        
2095
1581
        In addition, it converts the values from strings to their native
2096
1582
        types if their checks pass (and ``stringify`` is set).
2097
 
 
2098
 
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2099
 
        of a marking a fail with a ``False``, it will preserve the actual
2100
 
        exception object. This can contain info about the reason for failure.
2101
 
        For example the ``VdtValueTooSmallError`` indicates that the value
2102
 
        supplied was too small. If a value (or section) is missing it will
2103
 
        still be marked as ``False``.
2104
 
 
2105
 
        You must have the validate module to use ``preserve_errors=True``.
2106
 
 
2107
 
        You can then use the ``flatten_errors`` function to turn your nested
2108
 
        results dictionary into a flattened list of failures - useful for
2109
 
        displaying meaningful error messages.
 
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)
2110
1946
        """
2111
1947
        if section is None:
2112
1948
            if self.configspec is None:
2113
 
                raise ValueError('No configspec supplied.')
2114
 
            if preserve_errors:
2115
 
                # We do this once to remove a top level dependency on the validate module
2116
 
                # Which makes importing configobj faster
2117
 
                from validate import VdtMissingValue
2118
 
                self._vdtMissingValue = VdtMissingValue
2119
 
 
 
1949
                raise ValueError, 'No configspec supplied.'
2120
1950
            section = self
2121
 
 
2122
 
            if copy:
2123
 
                section.initial_comment = section.configspec.initial_comment
2124
 
                section.final_comment = section.configspec.final_comment
2125
 
                section.encoding = section.configspec.encoding
2126
 
                section.BOM = section.configspec.BOM
2127
 
                section.newlines = section.configspec.newlines
2128
 
                section.indent_type = section.configspec.indent_type
2129
 
 
2130
 
        #
2131
 
        configspec = section.configspec
2132
 
        self._set_configspec(section, copy)
2133
 
 
2134
 
        def validate_entry(entry, spec, val, missing, ret_true, ret_false):
 
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]
2135
1974
            try:
2136
 
                check = validator.check(spec,
 
1975
                check = validator.check(spec_section[entry],
2137
1976
                                        val,
2138
 
                                        missing=missing
2139
 
                                        )
2140
 
            except validator.baseErrorClass, e:
2141
 
                if not preserve_errors or isinstance(e, self._vdtMissingValue):
2142
 
                    out[entry] = False
2143
 
                else:
2144
 
                    # preserve the error
2145
 
                    out[entry] = e
2146
 
                    ret_false = False
 
1977
                                        missing=missing)
 
1978
            except validator.baseErrorClass:
 
1979
                out[entry] = False
2147
1980
                ret_true = False
 
1981
            # MIKE: we want to raise all other exceptions, not just print ?
 
1982
##            except Exception, err:
 
1983
##                print err
2148
1984
            else:
2149
 
                try:
2150
 
                    section.default_values.pop(entry, None)
2151
 
                except AttributeError:
2152
 
                    # For Python 2.2 compatibility
2153
 
                    try:
2154
 
                        del section.default_values[entry]
2155
 
                    except KeyError:
2156
 
                        pass
2157
 
 
2158
 
                try:
2159
 
                    section.default_values[entry] = validator.get_default_value(configspec[entry])
2160
 
                except (KeyError, AttributeError):
2161
 
                    # No default or validator has no 'get_default_value' (e.g. SimpleVal)
2162
 
                    pass
2163
 
 
2164
1985
                ret_false = False
2165
1986
                out[entry] = True
2166
1987
                if self.stringify or missing:
2169
1990
                    if not self.stringify:
2170
1991
                        if isinstance(check, (list, tuple)):
2171
1992
                            # preserve lists
2172
 
                            check = [self._str(item) for item in check]
 
1993
                            check = [str(item) for item in check]
2173
1994
                        elif missing and check is None:
2174
1995
                            # convert the None from a default to a ''
2175
1996
                            check = ''
2176
1997
                        else:
2177
 
                            check = self._str(check)
 
1998
                            check = str(check)
2178
1999
                    if (check != val) or missing:
2179
2000
                        section[entry] = check
2180
 
                if not copy and missing and entry not in section.defaults:
 
2001
                if missing and entry not in section.defaults:
2181
2002
                    section.defaults.append(entry)
2182
 
            return ret_true, ret_false
2183
 
 
2184
2003
        #
2185
 
        out = {}
2186
 
        ret_true = True
2187
 
        ret_false = True
2188
 
 
2189
 
        unvalidated = [k for k in section.scalars if k not in configspec]
2190
 
        incorrect_sections = [k for k in configspec.sections if k in section.scalars]
2191
 
        incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2192
 
 
2193
 
        for entry in configspec.scalars:
2194
 
            if entry in ('__many__', '___many___'):
2195
 
                # reserved names
2196
 
                continue
2197
 
 
2198
 
            if (not entry in section.scalars) or (entry in section.defaults):
2199
 
                # missing entries
2200
 
                # or entries from defaults
2201
 
                missing = True
2202
 
                val = None
2203
 
                if copy and not entry in section.scalars:
2204
 
                    # copy comments
2205
 
                    section.comments[entry] = (
2206
 
                        configspec.comments.get(entry, []))
2207
 
                    section.inline_comments[entry] = (
2208
 
                        configspec.inline_comments.get(entry, ''))
2209
 
                #
2210
 
            else:
2211
 
                missing = False
2212
 
                val = section[entry]
2213
 
 
2214
 
            ret_true, ret_false = validate_entry(entry, configspec[entry], val,
2215
 
                                                 missing, ret_true, ret_false)
2216
 
 
2217
 
        many = None
2218
 
        if '__many__' in configspec.scalars:
2219
 
            many = configspec['__many__']
2220
 
        elif '___many___' in configspec.scalars:
2221
 
            many = configspec['___many___']
2222
 
 
2223
 
        if many is not None:
2224
 
            for entry in unvalidated:
2225
 
                val = section[entry]
2226
 
                ret_true, ret_false = validate_entry(entry, many, val, False,
2227
 
                                                     ret_true, ret_false)
2228
 
 
2229
 
        for entry in incorrect_scalars:
2230
 
            ret_true = False
2231
 
            if not preserve_errors:
2232
 
                out[entry] = False
2233
 
            else:
2234
 
                ret_false = False
2235
 
                msg = 'Value %r was provided as a section' % entry
2236
 
                out[entry] = validator.baseErrorClass(msg)
2237
 
        for entry in incorrect_sections:
2238
 
            ret_true = False
2239
 
            if not preserve_errors:
2240
 
                out[entry] = False
2241
 
            else:
2242
 
                ret_false = False
2243
 
                msg = 'Section %r was provided as a single value' % entry
2244
 
                out[entry] = validator.baseErrorClass(msg)
2245
 
 
2246
 
        # Missing sections will have been created as empty ones when the
2247
 
        # configspec was read.
2248
2004
        for entry in section.sections:
2249
 
            # FIXME: this means DEFAULT is not copied in copy mode
2250
 
            if section is self and entry == 'DEFAULT':
2251
 
                continue
2252
 
            if section[entry].configspec is None:
2253
 
                continue
2254
 
            if copy:
2255
 
                section.comments[entry] = configspec.comments.get(entry, [])
2256
 
                section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
2257
 
            check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
 
2005
            check = self.validate(validator, section[entry])
2258
2006
            out[entry] = check
2259
2007
            if check == False:
2260
2008
                ret_true = False
2268
2016
            return True
2269
2017
        elif ret_false:
2270
2018
            return False
2271
 
        return out
2272
 
 
2273
 
 
2274
 
    def reset(self):
2275
 
        """Clear ConfigObj instance and restore to 'freshly created' state."""
2276
 
        self.clear()
2277
 
        self._initialise()
2278
 
        # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2279
 
        #        requires an empty dictionary
2280
 
        self.configspec = None
2281
 
        # Just to be sure ;-)
2282
 
        self._original_configspec = None
2283
 
 
2284
 
 
2285
 
    def reload(self):
2286
 
        """
2287
 
        Reload a ConfigObj from file.
2288
 
 
2289
 
        This method raises a ``ReloadError`` if the ConfigObj doesn't have
2290
 
        a filename attribute pointing to a file.
2291
 
        """
2292
 
        if not isinstance(self.filename, basestring):
2293
 
            raise ReloadError()
2294
 
 
2295
 
        filename = self.filename
2296
 
        current_options = {}
2297
 
        for entry in OPTION_DEFAULTS:
2298
 
            if entry == 'configspec':
2299
 
                continue
2300
 
            current_options[entry] = getattr(self, entry)
2301
 
 
2302
 
        configspec = self._original_configspec
2303
 
        current_options['configspec'] = configspec
2304
 
 
2305
 
        self.clear()
2306
 
        self._initialise(current_options)
2307
 
        self._load(filename, configspec)
2308
 
 
2309
 
 
 
2019
        else:
 
2020
            return out
2310
2021
 
2311
2022
class SimpleVal(object):
2312
2023
    """
2313
2024
    A simple validator.
2314
2025
    Can be used to check that all members expected are present.
2315
 
 
 
2026
    
2316
2027
    To use it, provide a configspec with all your members in (the value given
2317
2028
    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2318
2029
    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2319
2030
    members are present, or a dictionary with True/False meaning
2320
2031
    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
2321
2072
    """
2322
 
 
 
2073
    
2323
2074
    def __init__(self):
2324
2075
        self.baseErrorClass = ConfigObjError
2325
 
 
 
2076
    
2326
2077
    def check(self, check, member, missing=False):
2327
2078
        """A dummy check method, always returns the value unchanged."""
2328
2079
        if missing:
2329
 
            raise self.baseErrorClass()
 
2080
            raise self.baseErrorClass
2330
2081
        return member
2331
2082
 
2332
 
 
2333
 
# Check / processing functions for options
2334
 
def flatten_errors(cfg, res, levels=None, results=None):
2335
 
    """
2336
 
    An example function that will turn a nested dictionary of results
2337
 
    (as returned by ``ConfigObj.validate``) into a flat list.
2338
 
 
2339
 
    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2340
 
    dictionary returned by ``validate``.
2341
 
 
2342
 
    (This is a recursive function, so you shouldn't use the ``levels`` or
2343
 
    ``results`` arguments - they are used by the function.)
2344
 
 
2345
 
    Returns a list of keys that failed. Each member of the list is a tuple :
2346
 
 
2347
 
    ::
2348
 
 
2349
 
        ([list of sections...], key, result)
2350
 
 
2351
 
    If ``validate`` was called with ``preserve_errors=False`` (the default)
2352
 
    then ``result`` will always be ``False``.
2353
 
 
2354
 
    *list of sections* is a flattened list of sections that the key was found
2355
 
    in.
2356
 
 
2357
 
    If the section was missing (or a section was expected and a scalar provided
2358
 
    - or vice-versa) then key will be ``None``.
2359
 
 
2360
 
    If the value (or section) was missing then ``result`` will be ``False``.
2361
 
 
2362
 
    If ``validate`` was called with ``preserve_errors=True`` and a value
2363
 
    was present, but failed the check, then ``result`` will be the exception
2364
 
    object returned. You can use this as a string that describes the failure.
2365
 
 
2366
 
    For example *The value "3" is of the wrong type*.
2367
 
 
2368
 
    >>> import validate
2369
 
    >>> vtor = validate.Validator()
2370
 
    >>> my_ini = '''
2371
 
    ...     option1 = True
2372
 
    ...     [section1]
2373
 
    ...     option1 = True
2374
 
    ...     [section2]
2375
 
    ...     another_option = Probably
2376
 
    ...     [section3]
2377
 
    ...     another_option = True
2378
 
    ...     [[section3b]]
2379
 
    ...     value = 3
2380
 
    ...     value2 = a
2381
 
    ...     value3 = 11
2382
 
    ...     '''
2383
 
    >>> my_cfg = '''
2384
 
    ...     option1 = boolean()
2385
 
    ...     option2 = boolean()
2386
 
    ...     option3 = boolean(default=Bad_value)
2387
 
    ...     [section1]
2388
 
    ...     option1 = boolean()
2389
 
    ...     option2 = boolean()
2390
 
    ...     option3 = boolean(default=Bad_value)
2391
 
    ...     [section2]
2392
 
    ...     another_option = boolean()
2393
 
    ...     [section3]
2394
 
    ...     another_option = boolean()
2395
 
    ...     [[section3b]]
2396
 
    ...     value = integer
2397
 
    ...     value2 = integer
2398
 
    ...     value3 = integer(0, 10)
2399
 
    ...         [[[section3b-sub]]]
2400
 
    ...         value = string
2401
 
    ...     [section4]
2402
 
    ...     another_option = boolean()
2403
 
    ...     '''
2404
 
    >>> cs = my_cfg.split('\\n')
2405
 
    >>> ini = my_ini.split('\\n')
2406
 
    >>> cfg = ConfigObj(ini, configspec=cs)
2407
 
    >>> res = cfg.validate(vtor, preserve_errors=True)
2408
 
    >>> errors = []
2409
 
    >>> for entry in flatten_errors(cfg, res):
2410
 
    ...     section_list, key, error = entry
2411
 
    ...     section_list.insert(0, '[root]')
2412
 
    ...     if key is not None:
2413
 
    ...        section_list.append(key)
2414
 
    ...     else:
2415
 
    ...         section_list.append('[missing]')
2416
 
    ...     section_string = ', '.join(section_list)
2417
 
    ...     errors.append((section_string, ' = ', error))
2418
 
    >>> errors.sort()
2419
 
    >>> for entry in errors:
2420
 
    ...     print entry[0], entry[1], (entry[2] or 0)
2421
 
    [root], option2  =  0
2422
 
    [root], option3  =  the value "Bad_value" is of the wrong type.
2423
 
    [root], section1, option2  =  0
2424
 
    [root], section1, option3  =  the value "Bad_value" is of the wrong type.
2425
 
    [root], section2, another_option  =  the value "Probably" is of the wrong type.
2426
 
    [root], section3, section3b, section3b-sub, [missing]  =  0
2427
 
    [root], section3, section3b, value2  =  the value "a" is of the wrong type.
2428
 
    [root], section3, section3b, value3  =  the value "11" is too big.
2429
 
    [root], section4, [missing]  =  0
2430
 
    """
2431
 
    if levels is None:
2432
 
        # first time called
2433
 
        levels = []
2434
 
        results = []
2435
 
    if res is True:
2436
 
        return results
2437
 
    if res is False or isinstance(res, Exception):
2438
 
        results.append((levels[:], None, res))
2439
 
        if levels:
2440
 
            levels.pop()
2441
 
        return results
2442
 
    for (key, val) in res.items():
2443
 
        if val == True:
2444
 
            continue
2445
 
        if isinstance(cfg.get(key), dict):
2446
 
            # Go down one level
2447
 
            levels.append(key)
2448
 
            flatten_errors(cfg[key], val, levels, results)
2449
 
            continue
2450
 
        results.append((levels[:], key, val))
2451
 
    #
2452
 
    # Go up one level
2453
 
    if levels:
2454
 
        levels.pop()
2455
 
    #
2456
 
    return results
2457
 
 
2458
 
 
2459
 
"""*A programming language is a medium of expression.* - Paul Graham"""
 
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