~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Martin Pool
  • Date: 2007-10-24 02:33:14 UTC
  • mto: This revision was merged to the branch mainline in revision 2933.
  • Revision ID: mbp@sourcefrog.net-20071024023314-l9mscm8wsb1bvj1f
typo

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
 
16
16
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
17
17
# Comments, suggestions and bug reports welcome.
18
18
 
19
 
 
20
19
from __future__ import generators
21
20
 
 
21
"""
 
22
    >>> z = ConfigObj()
 
23
    >>> z['a'] = 'a'
 
24
    >>> z['sect'] = {
 
25
    ...    'subsect': {
 
26
    ...         'a': 'fish',
 
27
    ...         'b': 'wobble',
 
28
    ...     },
 
29
    ...     'member': 'value',
 
30
    ... }
 
31
    >>> x = ConfigObj(z.write())
 
32
    >>> z == x
 
33
    1
 
34
"""
 
35
 
22
36
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
 
37
INTP_VER = sys.version_info[:2]
 
38
if INTP_VER < (2, 2):
 
39
    raise RuntimeError("Python v.2.2 or later needed")
 
40
 
 
41
import os, re
 
42
from types import StringTypes
 
43
from warnings import warn
 
44
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
54
45
 
55
46
# A dictionary mapping BOM to
56
47
# the encoding to decode with, and what to set the
91
82
    None: BOM_UTF8
92
83
    }
93
84
 
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'''"
 
85
try:
 
86
    from validate import VdtMissingValue
 
87
except ImportError:
 
88
    VdtMissingValue = None
106
89
 
107
90
try:
108
91
    enumerate
114
97
            i += 1
115
98
            yield i, item
116
99
 
117
 
# Sentinel for use in getattr calls to replace hasattr
118
 
MISSING = object()
119
 
 
120
 
__version__ = '4.6.0'
 
100
try:
 
101
    True, False
 
102
except NameError:
 
103
    True, False = 1, 0
 
104
 
 
105
 
 
106
__version__ = '4.2.0beta2'
121
107
 
122
108
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
123
109
 
124
110
__docformat__ = "restructuredtext en"
125
111
 
 
112
# NOTE: Does it make sense to have the following in __all__ ?
 
113
# NOTE: DEFAULT_INDENT_TYPE, NUM_INDENT_SPACES, MAX_INTERPOL_DEPTH
 
114
# NOTE: If used as from configobj import...
 
115
# NOTE: They are effectively read only
126
116
__all__ = (
127
117
    '__version__',
128
118
    'DEFAULT_INDENT_TYPE',
129
 
    'DEFAULT_INTERPOLATION',
 
119
    'NUM_INDENT_SPACES',
 
120
    'MAX_INTERPOL_DEPTH',
130
121
    'ConfigObjError',
131
122
    'NestingError',
132
123
    'ParseError',
135
126
    'ConfigObj',
136
127
    'SimpleVal',
137
128
    'InterpolationError',
138
 
    'InterpolationLoopError',
 
129
    'InterpolationDepthError',
139
130
    'MissingInterpolationOption',
140
131
    'RepeatSectionError',
141
 
    'ReloadError',
142
 
    'UnreprError',
143
 
    'UnknownType',
144
132
    '__docformat__',
145
133
    'flatten_errors',
146
134
)
147
135
 
148
 
DEFAULT_INTERPOLATION = 'configparser'
149
 
DEFAULT_INDENT_TYPE = '    '
 
136
DEFAULT_INDENT_TYPE = ' '
 
137
NUM_INDENT_SPACES = 4
150
138
MAX_INTERPOL_DEPTH = 10
151
139
 
152
140
OPTION_DEFAULTS = {
161
149
    'indent_type': None,
162
150
    'encoding': None,
163
151
    'default_encoding': None,
164
 
    'unrepr': False,
165
 
    'write_empty_values': False,
166
152
}
167
153
 
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
154
class ConfigObjError(SyntaxError):
249
155
    """
250
156
    This is the base class for all errors that ConfigObj raises.
251
157
    It is a subclass of SyntaxError.
 
158
    
 
159
    >>> raise ConfigObjError
 
160
    Traceback (most recent call last):
 
161
    ConfigObjError
252
162
    """
253
163
    def __init__(self, message='', line_number=None, line=''):
254
164
        self.line = line
255
165
        self.line_number = line_number
 
166
        self.message = message
256
167
        SyntaxError.__init__(self, message)
257
168
 
258
 
 
259
169
class NestingError(ConfigObjError):
260
170
    """
261
171
    This error indicates a level of nesting that doesn't match.
 
172
    
 
173
    >>> raise NestingError
 
174
    Traceback (most recent call last):
 
175
    NestingError
262
176
    """
263
177
 
264
 
 
265
178
class ParseError(ConfigObjError):
266
179
    """
267
180
    This error indicates that a line is badly written.
268
181
    It is neither a valid ``key = value`` line,
269
182
    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
 
 
 
183
    
 
184
    >>> raise ParseError
 
185
    Traceback (most recent call last):
 
186
    ParseError
 
187
    """
281
188
 
282
189
class DuplicateError(ConfigObjError):
283
190
    """
284
191
    The keyword or section specified already exists.
 
192
    
 
193
    >>> raise DuplicateError
 
194
    Traceback (most recent call last):
 
195
    DuplicateError
285
196
    """
286
197
 
287
 
 
288
198
class ConfigspecError(ConfigObjError):
289
199
    """
290
200
    An error occured whilst parsing a configspec.
 
201
    
 
202
    >>> raise ConfigspecError
 
203
    Traceback (most recent call last):
 
204
    ConfigspecError
291
205
    """
292
206
 
293
 
 
294
207
class InterpolationError(ConfigObjError):
295
208
    """Base class for the two interpolation errors."""
296
209
 
297
 
 
298
 
class InterpolationLoopError(InterpolationError):
 
210
class InterpolationDepthError(InterpolationError):
299
211
    """Maximum interpolation depth exceeded in string interpolation."""
300
212
 
301
213
    def __init__(self, option):
 
214
        """
 
215
        >>> raise InterpolationDepthError('yoda')
 
216
        Traceback (most recent call last):
 
217
        InterpolationDepthError: max interpolation depth exceeded in value "yoda".
 
218
        """
302
219
        InterpolationError.__init__(
303
220
            self,
304
 
            'interpolation loop detected in value "%s".' % option)
305
 
 
 
221
            'max interpolation depth exceeded in value "%s".' % option)
306
222
 
307
223
class RepeatSectionError(ConfigObjError):
308
224
    """
309
225
    This error indicates additional sections in a section with a
310
226
    ``__many__`` (repeated) section.
 
227
    
 
228
    >>> raise RepeatSectionError
 
229
    Traceback (most recent call last):
 
230
    RepeatSectionError
311
231
    """
312
232
 
313
 
 
314
233
class MissingInterpolationOption(InterpolationError):
315
234
    """A value specified for interpolation was missing."""
316
235
 
317
236
    def __init__(self, option):
 
237
        """
 
238
        >>> raise MissingInterpolationOption('yoda')
 
239
        Traceback (most recent call last):
 
240
        MissingInterpolationOption: missing option "yoda" in interpolation.
 
241
        """
318
242
        InterpolationError.__init__(
319
243
            self,
320
244
            'missing option "%s" in interpolation.' % option)
321
245
 
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
246
class Section(dict):
494
247
    """
495
248
    A dictionary-like object that represents a section in a config file.
496
 
 
497
 
    It does string interpolation if the 'interpolation' attribute
 
249
    
 
250
    It does string interpolation if the 'interpolate' attribute
498
251
    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
 
 
 
252
    
 
253
    Interpolation is tried first from the 'DEFAULT' section of this object,
 
254
    next from the 'DEFAULT' section of the parent, lastly the main object.
 
255
    
504
256
    A Section will behave like an ordered dictionary - following the
505
257
    order of the ``scalars`` and ``sections`` attributes.
506
258
    You can use this to change the order of members.
507
 
 
 
259
    
508
260
    Iteration follows the order: scalars, then sections.
509
261
    """
510
262
 
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
 
 
 
263
    _KEYCRE = re.compile(r"%\(([^)]*)\)s|.")
520
264
 
521
265
    def __init__(self, parent, depth, main, indict=None, name=None):
522
266
        """
534
278
        self.main = main
535
279
        # level of nesting depth of this Section
536
280
        self.depth = depth
 
281
        # the sequence of scalar values in this Section
 
282
        self.scalars = []
 
283
        # the sequence of sections in this Section
 
284
        self.sections = []
537
285
        # purely for information
538
286
        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
287
        # for comments :-)
553
288
        self.comments = {}
554
289
        self.inline_comments = {}
555
 
        # the configspec
556
 
        self.configspec = None
 
290
        # for the configspec
 
291
        self.configspec = {}
557
292
        # for defaults
558
293
        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
 
294
        #
 
295
        # we do this explicitly so that __setitem__ is used properly
 
296
        # (rather than just passing to ``dict.__init__``)
 
297
        for entry in indict:
 
298
            self[entry] = indict[entry]
 
299
 
 
300
    def _interpolate(self, value):
 
301
        """Nicked from ConfigParser."""
 
302
        depth = MAX_INTERPOL_DEPTH
 
303
        # loop through this until it's done
 
304
        while depth:
 
305
            depth -= 1
 
306
            if value.find("%(") != -1:
 
307
                value = self._KEYCRE.sub(self._interpolation_replace, value)
578
308
            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)
 
309
                break
 
310
        else:
 
311
            raise InterpolationDepthError(value)
 
312
        return value
583
313
 
 
314
    def _interpolation_replace(self, match):
 
315
        """ """
 
316
        s = match.group(1)
 
317
        if s is None:
 
318
            return match.group()
 
319
        else:
 
320
            # switch off interpolation before we try and fetch anything !
 
321
            self.main.interpolation = False
 
322
            # try the 'DEFAULT' member of *this section* first
 
323
            val = self.get('DEFAULT', {}).get(s)
 
324
            # try the 'DEFAULT' member of the *parent section* next
 
325
            if val is None:
 
326
                val = self.parent.get('DEFAULT', {}).get(s)
 
327
            # last, try the 'DEFAULT' member of the *main section*
 
328
            if val is None:
 
329
                val = self.main.get('DEFAULT', {}).get(s)
 
330
            self.main.interpolation = True
 
331
            if val is None:
 
332
                raise MissingInterpolationOption(s)
 
333
            return val
584
334
 
585
335
    def __getitem__(self, key):
586
336
        """Fetch the item and do string interpolation."""
587
337
        val = dict.__getitem__(self, key)
588
 
        if self.main.interpolation and isinstance(val, basestring):
589
 
            return self._interpolate(key, val)
 
338
        if self.main.interpolation and isinstance(val, StringTypes):
 
339
            return self._interpolate(val)
590
340
        return val
591
341
 
592
 
 
593
 
    def __setitem__(self, key, value, unrepr=False):
 
342
    def __setitem__(self, key, value):
594
343
        """
595
344
        Correctly set a value.
596
 
 
 
345
        
597
346
        Making dictionary values Section instances.
598
347
        (We have to special case 'Section' instances - which are also dicts)
599
 
 
 
348
        
600
349
        Keys must be strings.
601
350
        Values need only be strings (or lists of strings) if
602
351
        ``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
352
        """
607
 
        if not isinstance(key, basestring):
608
 
            raise ValueError('The key "%s" is not a string.' % key)
609
 
 
 
353
        if not isinstance(key, StringTypes):
 
354
            raise ValueError, 'The key "%s" is not a string.' % key
610
355
        # add the comment
611
356
        if key not in self.comments:
612
357
            self.comments[key] = []
619
364
            if key not in self:
620
365
                self.sections.append(key)
621
366
            dict.__setitem__(self, key, value)
622
 
        elif isinstance(value, dict) and not unrepr:
 
367
        elif isinstance(value, dict):
623
368
            # First create the new depth level,
624
369
            # then create the section
625
370
            if key not in self:
638
383
            if key not in self:
639
384
                self.scalars.append(key)
640
385
            if not self.main.stringify:
641
 
                if isinstance(value, basestring):
 
386
                if isinstance(value, StringTypes):
642
387
                    pass
643
388
                elif isinstance(value, (list, tuple)):
644
389
                    for entry in value:
645
 
                        if not isinstance(entry, basestring):
646
 
                            raise TypeError('Value is not a string "%s".' % entry)
 
390
                        if not isinstance(entry, StringTypes):
 
391
                            raise TypeError, (
 
392
                                'Value is not a string "%s".' % entry)
647
393
                else:
648
 
                    raise TypeError('Value is not a string "%s".' % value)
 
394
                    raise TypeError, 'Value is not a string "%s".' % value
649
395
            dict.__setitem__(self, key, value)
650
396
 
651
 
 
652
397
    def __delitem__(self, key):
653
398
        """Remove items from the sequence when deleting."""
654
399
        dict. __delitem__(self, key)
659
404
        del self.comments[key]
660
405
        del self.inline_comments[key]
661
406
 
662
 
 
663
407
    def get(self, key, default=None):
664
408
        """A version of ``get`` that doesn't bypass string interpolation."""
665
409
        try:
667
411
        except KeyError:
668
412
            return default
669
413
 
670
 
 
671
414
    def update(self, indict):
672
415
        """
673
416
        A version of update that uses our ``__setitem__``.
677
420
 
678
421
 
679
422
    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
 
        """
 
423
        """ """
684
424
        val = dict.pop(self, key, *args)
685
425
        if key in self.scalars:
686
426
            del self.comments[key]
690
430
            del self.comments[key]
691
431
            del self.inline_comments[key]
692
432
            self.sections.remove(key)
693
 
        if self.main.interpolation and isinstance(val, basestring):
694
 
            return self._interpolate(key, val)
 
433
        if self.main.interpolation and isinstance(val, StringTypes):
 
434
            return self._interpolate(val)
695
435
        return val
696
436
 
697
 
 
698
437
    def popitem(self):
699
438
        """Pops the first (key,val)"""
700
439
        sequence = (self.scalars + self.sections)
701
440
        if not sequence:
702
 
            raise KeyError(": 'popitem(): dictionary is empty'")
 
441
            raise KeyError, ": 'popitem(): dictionary is empty'"
703
442
        key = sequence[0]
704
443
        val =  self[key]
705
444
        del self[key]
706
445
        return key, val
707
446
 
708
 
 
709
447
    def clear(self):
710
448
        """
711
449
        A version of clear that also affects scalars/sections
712
450
        Also clears comments and configspec.
713
 
 
 
451
        
714
452
        Leaves other attributes alone :
715
453
            depth/main/parent are not affected
716
454
        """
719
457
        self.sections = []
720
458
        self.comments = {}
721
459
        self.inline_comments = {}
722
 
        self.configspec = None
723
 
 
 
460
        self.configspec = {}
724
461
 
725
462
    def setdefault(self, key, default=None):
726
463
        """A version of setdefault that sets sequence if appropriate."""
730
467
            self[key] = default
731
468
            return self[key]
732
469
 
733
 
 
734
470
    def items(self):
735
 
        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
 
471
        """ """
736
472
        return zip((self.scalars + self.sections), self.values())
737
473
 
738
 
 
739
474
    def keys(self):
740
 
        """D.keys() -> list of D's keys"""
 
475
        """ """
741
476
        return (self.scalars + self.sections)
742
477
 
743
 
 
744
478
    def values(self):
745
 
        """D.values() -> list of D's values"""
 
479
        """ """
746
480
        return [self[key] for key in (self.scalars + self.sections)]
747
481
 
748
 
 
749
482
    def iteritems(self):
750
 
        """D.iteritems() -> an iterator over the (key, value) items of D"""
 
483
        """ """
751
484
        return iter(self.items())
752
485
 
753
 
 
754
486
    def iterkeys(self):
755
 
        """D.iterkeys() -> an iterator over the keys of D"""
 
487
        """ """
756
488
        return iter((self.scalars + self.sections))
757
489
 
758
490
    __iter__ = iterkeys
759
491
 
760
 
 
761
492
    def itervalues(self):
762
 
        """D.itervalues() -> an iterator over the values of D"""
 
493
        """ """
763
494
        return iter(self.values())
764
495
 
765
 
 
766
496
    def __repr__(self):
767
 
        """x.__repr__() <==> repr(x)"""
768
497
        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
769
498
            for key in (self.scalars + self.sections)])
770
499
 
771
500
    __str__ = __repr__
772
 
    __str__.__doc__ = "x.__str__() <==> str(x)"
773
 
 
774
501
 
775
502
    # Extra methods - not in a normal dictionary
776
503
 
777
504
    def dict(self):
778
505
        """
779
506
        Return a deepcopy of self as a dictionary.
780
 
 
 
507
        
781
508
        All members that are ``Section`` instances are recursively turned to
782
509
        ordinary dictionaries - by calling their ``dict`` method.
783
 
 
 
510
        
784
511
        >>> n = a.dict()
785
512
        >>> n == a
786
513
        1
792
519
            this_entry = self[entry]
793
520
            if isinstance(this_entry, Section):
794
521
                this_entry = this_entry.dict()
795
 
            elif isinstance(this_entry, list):
 
522
            elif isinstance(this_entry, (list, tuple)):
796
523
                # create a copy rather than a reference
797
524
                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
525
            newdict[entry] = this_entry
802
526
        return newdict
803
527
 
804
 
 
805
528
    def merge(self, indict):
806
529
        """
807
530
        A recursive update - useful for merging config files.
808
 
 
 
531
        
809
532
        >>> a = '''[section1]
810
533
        ...     option1 = True
811
534
        ...     [[subsection]]
819
542
        >>> c2 = ConfigObj(a)
820
543
        >>> c2.merge(c1)
821
544
        >>> c2
822
 
        ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
 
545
        {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
823
546
        """
824
547
        for key, val in indict.items():
825
548
            if (key in self and isinstance(self[key], dict) and
826
549
                                isinstance(val, dict)):
827
550
                self[key].merge(val)
828
 
            else:
 
551
            else:   
829
552
                self[key] = val
830
553
 
831
 
 
832
554
    def rename(self, oldkey, newkey):
833
555
        """
834
556
        Change a keyname to another, without changing position in sequence.
835
 
 
 
557
        
836
558
        Implemented so that transformations can be made on keys,
837
559
        as well as on values. (used by encode and decode)
838
 
 
 
560
        
839
561
        Also renames comments.
840
562
        """
841
563
        if oldkey in self.scalars:
843
565
        elif oldkey in self.sections:
844
566
            the_list = self.sections
845
567
        else:
846
 
            raise KeyError('Key "%s" not found.' % oldkey)
 
568
            raise KeyError, 'Key "%s" not found.' % oldkey
847
569
        pos = the_list.index(oldkey)
848
570
        #
849
571
        val = self[oldkey]
858
580
        self.comments[newkey] = comm
859
581
        self.inline_comments[newkey] = inline_comment
860
582
 
861
 
 
862
583
    def walk(self, function, raise_errors=True,
863
584
            call_on_sections=False, **keywargs):
864
585
        """
865
586
        Walk every member and call a function on the keyword and value.
866
 
 
 
587
        
867
588
        Return a dictionary of the return values
868
 
 
 
589
        
869
590
        If the function raises an exception, raise the errror
870
591
        unless ``raise_errors=False``, in which case set the return value to
871
592
        ``False``.
872
 
 
 
593
        
873
594
        Any unrecognised keyword arguments you pass to walk, will be pased on
874
595
        to the function you pass in.
875
 
 
 
596
        
876
597
        Note: if ``call_on_sections`` is ``True`` then - on encountering a
877
598
        subsection, *first* the function is called for the *whole* subsection,
878
599
        and then recurses into it's members. This means your function must be
879
600
        able to handle strings, dictionaries and lists. This allows you
880
601
        to change the key of subsections as well as for ordinary members. The
881
602
        return value when called on the whole subsection has to be discarded.
882
 
 
 
603
        
883
604
        See  the encode and decode methods for examples, including functions.
884
 
 
885
 
        .. admonition:: caution
886
 
 
 
605
        
 
606
        .. caution::
 
607
        
887
608
            You can use ``walk`` to transform the names of members of a section
888
609
            but you mustn't add or delete members.
889
 
 
 
610
        
890
611
        >>> config = '''[XXXXsection]
891
612
        ... XXXXkey = XXXXvalue'''.splitlines()
892
613
        >>> cfg = ConfigObj(config)
893
614
        >>> cfg
894
 
        ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
 
615
        {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
895
616
        >>> def transform(section, key):
896
617
        ...     val = section[key]
897
618
        ...     newkey = key.replace('XXXX', 'CLIENT1')
904
625
        >>> cfg.walk(transform, call_on_sections=True)
905
626
        {'CLIENT1section': {'CLIENT1key': None}}
906
627
        >>> cfg
907
 
        ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
 
628
        {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
908
629
        """
909
630
        out = {}
910
631
        # scalars first
943
664
                **keywargs)
944
665
        return out
945
666
 
 
667
    def decode(self, encoding):
 
668
        """
 
669
        Decode all strings and values to unicode, using the specified encoding.
 
670
        
 
671
        Works with subsections and list values.
 
672
        
 
673
        Uses the ``walk`` method.
 
674
        
 
675
        Testing ``encode`` and ``decode``.
 
676
        >>> m = ConfigObj(a)
 
677
        >>> m.decode('ascii')
 
678
        >>> def testuni(val):
 
679
        ...     for entry in val:
 
680
        ...         if not isinstance(entry, unicode):
 
681
        ...             sys.stderr.write(type(entry))
 
682
        ...             sys.stderr.write('\n')
 
683
        ...             raise AssertionError, 'decode failed.'
 
684
        ...         if isinstance(val[entry], dict):
 
685
        ...             testuni(val[entry])
 
686
        ...         elif not isinstance(val[entry], unicode):
 
687
        ...             raise AssertionError, 'decode failed.'
 
688
        >>> testuni(m)
 
689
        >>> m.encode('ascii')
 
690
        >>> a == m
 
691
        1
 
692
        """
 
693
        def decode(section, key, encoding=encoding):
 
694
            """ """
 
695
            val = section[key]
 
696
            if isinstance(val, (list, tuple)):
 
697
                newval = []
 
698
                for entry in val:
 
699
                    newval.append(entry.decode(encoding))
 
700
            elif isinstance(val, dict):
 
701
                newval = val
 
702
            else:
 
703
                newval = val.decode(encoding)
 
704
            newkey = key.decode(encoding)
 
705
            section.rename(key, newkey)
 
706
            section[newkey] = newval
 
707
        # using ``call_on_sections`` allows us to modify section names
 
708
        self.walk(decode, call_on_sections=True)
 
709
 
 
710
    def encode(self, encoding):
 
711
        """
 
712
        Encode all strings and values from unicode,
 
713
        using the specified encoding.
 
714
        
 
715
        Works with subsections and list values.
 
716
        Uses the ``walk`` method.
 
717
        """
 
718
        def encode(section, key, encoding=encoding):
 
719
            """ """
 
720
            val = section[key]
 
721
            if isinstance(val, (list, tuple)):
 
722
                newval = []
 
723
                for entry in val:
 
724
                    newval.append(entry.encode(encoding))
 
725
            elif isinstance(val, dict):
 
726
                newval = val
 
727
            else:
 
728
                newval = val.encode(encoding)
 
729
            newkey = key.encode(encoding)
 
730
            section.rename(key, newkey)
 
731
            section[newkey] = newval
 
732
        self.walk(encode, call_on_sections=True)
 
733
 
 
734
    def istrue(self, key):
 
735
        """A deprecated version of ``as_bool``."""
 
736
        warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
 
737
                'instead.', DeprecationWarning)
 
738
        return self.as_bool(key)
946
739
 
947
740
    def as_bool(self, key):
948
741
        """
949
742
        Accepts a key as input. The corresponding value must be a string or
950
743
        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
951
744
        retain compatibility with Python 2.2.
952
 
 
953
 
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns
 
745
        
 
746
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
954
747
        ``True``.
955
 
 
956
 
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns
 
748
        
 
749
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
957
750
        ``False``.
958
 
 
 
751
        
959
752
        ``as_bool`` is not case sensitive.
960
 
 
 
753
        
961
754
        Any other input will raise a ``ValueError``.
962
 
 
 
755
        
963
756
        >>> a = ConfigObj()
964
757
        >>> a['a'] = 'fish'
965
758
        >>> a.as_bool('a')
979
772
            return False
980
773
        else:
981
774
            try:
982
 
                if not isinstance(val, basestring):
983
 
                    # TODO: Why do we raise a KeyError here?
984
 
                    raise KeyError()
 
775
                if not isinstance(val, StringTypes):
 
776
                    raise KeyError
985
777
                else:
986
778
                    return self.main._bools[val.lower()]
987
779
            except KeyError:
988
780
                raise ValueError('Value "%s" is neither True nor False' % val)
989
781
 
990
 
 
991
782
    def as_int(self, key):
992
783
        """
993
784
        A convenience method which coerces the specified value to an integer.
994
 
 
 
785
        
995
786
        If the value is an invalid literal for ``int``, a ``ValueError`` will
996
787
        be raised.
997
 
 
 
788
        
998
789
        >>> a = ConfigObj()
999
790
        >>> a['a'] = 'fish'
1000
791
        >>> a.as_int('a')
1001
792
        Traceback (most recent call last):
1002
 
        ValueError: invalid literal for int() with base 10: 'fish'
 
793
        ValueError: invalid literal for int(): fish
1003
794
        >>> a['b'] = '1'
1004
795
        >>> a.as_int('b')
1005
796
        1
1006
797
        >>> a['b'] = '3.2'
1007
798
        >>> a.as_int('b')
1008
799
        Traceback (most recent call last):
1009
 
        ValueError: invalid literal for int() with base 10: '3.2'
 
800
        ValueError: invalid literal for int(): 3.2
1010
801
        """
1011
802
        return int(self[key])
1012
803
 
1013
 
 
1014
804
    def as_float(self, key):
1015
805
        """
1016
806
        A convenience method which coerces the specified value to a float.
1017
 
 
 
807
        
1018
808
        If the value is an invalid literal for ``float``, a ``ValueError`` will
1019
809
        be raised.
1020
 
 
 
810
        
1021
811
        >>> a = ConfigObj()
1022
812
        >>> a['a'] = 'fish'
1023
813
        >>> a.as_float('a')
1031
821
        3.2000000000000002
1032
822
        """
1033
823
        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
 
 
 
824
    
1090
825
 
1091
826
class ConfigObj(Section):
1092
 
    """An object to read, create, and write config files."""
 
827
    """
 
828
    An object to read, create, and write config files.
 
829
    
 
830
    Testing with duplicate keys and sections.
 
831
    
 
832
    >>> c = '''
 
833
    ... [hello]
 
834
    ... member = value
 
835
    ... [hello again]
 
836
    ... member = value
 
837
    ... [ "hello" ]
 
838
    ... member = value
 
839
    ... '''
 
840
    >>> ConfigObj(c.split('\\n'), raise_errors = True)
 
841
    Traceback (most recent call last):
 
842
    DuplicateError: Duplicate section name at line 5.
 
843
    
 
844
    >>> d = '''
 
845
    ... [hello]
 
846
    ... member = value
 
847
    ... [hello again]
 
848
    ... member1 = value
 
849
    ... member2 = value
 
850
    ... 'member1' = value
 
851
    ... [ "and again" ]
 
852
    ... member = value
 
853
    ... '''
 
854
    >>> ConfigObj(d.split('\\n'), raise_errors = True)
 
855
    Traceback (most recent call last):
 
856
    DuplicateError: Duplicate keyword name at line 6.
 
857
    """
1093
858
 
1094
859
    _keyword = re.compile(r'''^ # line start
1095
860
        (\s*)                   # indentation
1119
884
 
1120
885
    # this regexp pulls list values out as a single string
1121
886
    # or single values and comments
1122
 
    # FIXME: this regex adds a '' to the end of comma terminated lists
1123
 
    #   workaround in ``_handle_value``
1124
887
    _valueexp = re.compile(r'''^
1125
888
        (?:
1126
889
            (?:
1129
892
                        (?:
1130
893
                            (?:".*?")|              # double quotes
1131
894
                            (?:'.*?')|              # single quotes
1132
 
                            (?:[^'",\#][^,\#]*?)    # unquoted
 
895
                            (?:[^'",\#][^,\#]*?)       # unquoted
1133
896
                        )
1134
897
                        \s*,\s*                     # comma
1135
898
                    )*      # match all list items ending in a comma (if any)
1137
900
                (
1138
901
                    (?:".*?")|                      # double quotes
1139
902
                    (?:'.*?')|                      # single quotes
1140
 
                    (?:[^'",\#\s][^,]*?)|           # unquoted
1141
 
                    (?:(?<!,))                      # Empty value
 
903
                    (?:[^'",\#\s][^,]*?)             # unquoted
1142
904
                )?          # last item in a list - or string value
1143
905
            )|
1144
906
            (,)             # alternatively a single comma - empty list
1164
926
        (
1165
927
            (?:".*?")|          # double quotes
1166
928
            (?:'.*?')|          # single quotes
1167
 
            (?:[^'"\#].*?)|     # unquoted
1168
 
            (?:)                # Empty value
 
929
            (?:[^'"\#].*?)      # unquoted
1169
930
        )
1170
931
        \s*(\#.*)?              # optional comment
1171
932
        $''',
1190
951
        'true': True, 'false': False,
1191
952
        }
1192
953
 
1193
 
 
1194
 
    def __init__(self, infile=None, options=None, _inspec=False, **kwargs):
 
954
    def __init__(self, infile=None, options=None, **kwargs):
1195
955
        """
1196
 
        Parse a config file or create a config file object.
1197
 
 
 
956
        Parse or create a config file object.
 
957
        
1198
958
        ``ConfigObj(infile=None, options=None, **kwargs)``
1199
959
        """
1200
 
        self._inspec = _inspec
 
960
        if infile is None:
 
961
            infile = []
 
962
        if options is None:
 
963
            options = {}
 
964
        # keyword arguments take precedence over an options dictionary
 
965
        options.update(kwargs)
1201
966
        # init the superclass
1202
967
        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
 
 
 
968
        #
1212
969
        defaults = OPTION_DEFAULTS.copy()
 
970
        for entry in options.keys():
 
971
            if entry not in defaults.keys():
 
972
                raise TypeError, 'Unrecognised option "%s".' % entry
1213
973
        # TODO: check the values too.
1214
 
        for entry in options:
1215
 
            if entry not in defaults:
1216
 
                raise TypeError('Unrecognised option "%s".' % entry)
1217
 
 
 
974
        #
1218
975
        # Add any explicit options to the defaults
1219
976
        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):
 
977
        #
 
978
        # initialise a few variables
 
979
        self.filename = None
 
980
        self._errors = []
 
981
        self.raise_errors = defaults['raise_errors']
 
982
        self.interpolation = defaults['interpolation']
 
983
        self.list_values = defaults['list_values']
 
984
        self.create_empty = defaults['create_empty']
 
985
        self.file_error = defaults['file_error']
 
986
        self.stringify = defaults['stringify']
 
987
        self.indent_type = defaults['indent_type']
 
988
        self.encoding = defaults['encoding']
 
989
        self.default_encoding = defaults['default_encoding']
 
990
        self.BOM = False
 
991
        self.newlines = None
 
992
        #
 
993
        self.initial_comment = []
 
994
        self.final_comment = []
 
995
        #
 
996
        if isinstance(infile, StringTypes):
1228
997
            self.filename = infile
1229
998
            if os.path.isfile(infile):
1230
 
                h = open(infile, 'rb')
1231
 
                infile = h.read() or []
1232
 
                h.close()
 
999
                infile = open(infile).read() or []
1233
1000
            elif self.file_error:
1234
1001
                # raise an error if the file doesn't exist
1235
 
                raise IOError('Config file not found: "%s".' % self.filename)
 
1002
                raise IOError, 'Config file not found: "%s".' % self.filename
1236
1003
            else:
1237
1004
                # file doesn't already exist
1238
1005
                if self.create_empty:
1239
1006
                    # this is a good test that the filename specified
1240
 
                    # isn't impossible - like on a non-existent device
 
1007
                    # isn't impossible - like on a non existent device
1241
1008
                    h = open(infile, 'w')
1242
1009
                    h.write('')
1243
1010
                    h.close()
1244
1011
                infile = []
1245
 
 
1246
1012
        elif isinstance(infile, (list, tuple)):
1247
1013
            infile = list(infile)
1248
 
 
1249
1014
        elif isinstance(infile, dict):
1250
1015
            # initialise self
1251
1016
            # the Section class handles creating subsections
1252
1017
            if isinstance(infile, ConfigObj):
1253
1018
                # get a copy of our ConfigObj
1254
1019
                infile = infile.dict()
1255
 
 
1256
1020
            for entry in infile:
1257
1021
                self[entry] = infile[entry]
1258
1022
            del self._errors
1259
 
 
1260
 
            if configspec is not None:
1261
 
                self._handle_configspec(configspec)
 
1023
            if defaults['configspec'] is not None:
 
1024
                self._handle_configspec(defaults['configspec'])
1262
1025
            else:
1263
1026
                self.configspec = None
1264
1027
            return
1265
 
 
1266
 
        elif getattr(infile, 'read', MISSING) is not MISSING:
 
1028
        elif getattr(infile, 'read', None) is not None:
1267
1029
            # This supports file like objects
1268
1030
            infile = infile.read() or []
1269
1031
            # needs splitting into lines - but needs doing *after* decoding
1270
1032
            # in case it's not an 8 bit encoding
1271
1033
        else:
1272
 
            raise TypeError('infile must be a filename, file like object, or list of lines.')
1273
 
 
 
1034
            raise TypeError, ('infile must be a filename,'
 
1035
                ' file like object, or list of lines.')
 
1036
        #
1274
1037
        if infile:
1275
1038
            # don't do it for the empty ConfigObj
1276
1039
            infile = self._handle_bom(infile)
1279
1042
            # Set the newlines attribute (first line ending it finds)
1280
1043
            # and strip trailing '\n' or '\r' from lines
1281
1044
            for line in infile:
1282
 
                if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
 
1045
                if (not line) or (line[-1] not in '\r\n'):
1283
1046
                    continue
1284
1047
                for end in ('\r\n', '\n', '\r'):
1285
1048
                    if line.endswith(end):
1286
1049
                        self.newlines = end
1287
1050
                        break
1288
1051
                break
1289
 
 
1290
1052
            infile = [line.rstrip('\r\n') for line in infile]
1291
 
 
 
1053
        #
1292
1054
        self._parse(infile)
1293
1055
        # if we had any errors, now is the time to raise them
1294
1056
        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]
 
1057
            error = ConfigObjError("Parsing failed.")
1301
1058
            # set the errors attribute; it's a list of tuples:
1302
1059
            # (error_type, message, line_number)
1303
1060
            error.errors = self._errors
1306
1063
            raise error
1307
1064
        # delete private attributes
1308
1065
        del self._errors
1309
 
 
1310
 
        if configspec is None:
 
1066
        #
 
1067
        if defaults['configspec'] is None:
1311
1068
            self.configspec = None
1312
1069
        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
 
 
 
1070
            self._handle_configspec(defaults['configspec'])
1353
1071
 
1354
1072
    def _handle_bom(self, infile):
1355
1073
        """
1356
1074
        Handle any BOM, and decode if necessary.
1357
 
 
 
1075
        
1358
1076
        If an encoding is specified, that *must* be used - but the BOM should
1359
1077
        still be removed (and the BOM attribute set).
1360
 
 
 
1078
        
1361
1079
        (If the encoding is wrongly specified, then a BOM for an alternative
1362
1080
        encoding won't be discovered or removed.)
1363
 
 
 
1081
        
1364
1082
        If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1365
1083
        removed. The BOM attribute will be set. UTF16 will be decoded to
1366
1084
        unicode.
1367
 
 
 
1085
        
1368
1086
        NOTE: This method must not be called with an empty ``infile``.
1369
 
 
 
1087
        
1370
1088
        Specifying the *wrong* encoding is likely to cause a
1371
1089
        ``UnicodeDecodeError``.
1372
 
 
 
1090
        
1373
1091
        ``infile`` must always be returned as a list of lines, but may be
1374
1092
        passed in as a single string.
1375
1093
        """
1376
1094
        if ((self.encoding is not None) and
1377
1095
            (self.encoding.lower() not in BOM_LIST)):
1378
1096
            # No need to check for a BOM
1379
 
            # the encoding specified doesn't have one
 
1097
            # encoding specified doesn't have one
1380
1098
            # just decode
1381
1099
            return self._decode(infile, self.encoding)
1382
 
 
 
1100
        #
1383
1101
        if isinstance(infile, (list, tuple)):
1384
1102
            line = infile[0]
1385
1103
        else:
1401
1119
                        ##self.BOM = True
1402
1120
                        # Don't need to remove BOM
1403
1121
                        return self._decode(infile, encoding)
1404
 
 
 
1122
                #
1405
1123
                # If we get this far, will *probably* raise a DecodeError
1406
1124
                # As it doesn't appear to start with a BOM
1407
1125
                return self._decode(infile, self.encoding)
1408
 
 
 
1126
            #
1409
1127
            # Must be UTF8
1410
1128
            BOM = BOM_SET[enc]
1411
1129
            if not line.startswith(BOM):
1412
1130
                return self._decode(infile, self.encoding)
1413
 
 
 
1131
            #
1414
1132
            newline = line[len(BOM):]
1415
 
 
 
1133
            #
1416
1134
            # BOM removed
1417
1135
            if isinstance(infile, (list, tuple)):
1418
1136
                infile[0] = newline
1420
1138
                infile = newline
1421
1139
            self.BOM = True
1422
1140
            return self._decode(infile, self.encoding)
1423
 
 
 
1141
        #
1424
1142
        # No encoding specified - so we need to check for UTF8/UTF16
1425
1143
        for BOM, (encoding, final_encoding) in BOMS.items():
1426
1144
            if not line.startswith(BOM):
1438
1156
                    else:
1439
1157
                        infile = newline
1440
1158
                    # UTF8 - don't decode
1441
 
                    if isinstance(infile, basestring):
 
1159
                    if isinstance(infile, StringTypes):
1442
1160
                        return infile.splitlines(True)
1443
1161
                    else:
1444
1162
                        return infile
1445
1163
                # UTF16 - have to decode
1446
1164
                return self._decode(infile, encoding)
1447
 
 
 
1165
        #
1448
1166
        # No BOM discovered and no encoding specified, just return
1449
 
        if isinstance(infile, basestring):
 
1167
        if isinstance(infile, StringTypes):
1450
1168
            # infile read from a file will be a single string
1451
1169
            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
 
 
 
1170
        else:
 
1171
            return infile
 
1172
 
 
1173
    def _a_to_u(self, string):
 
1174
        """Decode ascii strings to unicode if a self.encoding is specified."""
 
1175
        if not self.encoding:
 
1176
            return string
 
1177
        else:
 
1178
            return string.decode('ascii')
1462
1179
 
1463
1180
    def _decode(self, infile, encoding):
1464
1181
        """
1465
1182
        Decode infile to unicode. Using the specified encoding.
1466
 
 
 
1183
        
1467
1184
        if is a string, it also needs converting to a list.
1468
1185
        """
1469
 
        if isinstance(infile, basestring):
 
1186
        if isinstance(infile, StringTypes):
1470
1187
            # can't be unicode
1471
1188
            # NOTE: Could raise a ``UnicodeDecodeError``
1472
1189
            return infile.decode(encoding).splitlines(True)
1478
1195
                infile[i] = line.decode(encoding)
1479
1196
        return infile
1480
1197
 
1481
 
 
1482
1198
    def _decode_element(self, line):
1483
1199
        """Decode element to unicode if necessary."""
1484
1200
        if not self.encoding:
1487
1203
            return line.decode(self.default_encoding)
1488
1204
        return line
1489
1205
 
1490
 
 
1491
1206
    def _str(self, value):
1492
1207
        """
1493
1208
        Used by ``stringify`` within validate, to turn non-string values
1494
1209
        into strings.
1495
1210
        """
1496
 
        if not isinstance(value, basestring):
 
1211
        if not isinstance(value, StringTypes):
1497
1212
            return str(value)
1498
1213
        else:
1499
1214
            return value
1500
1215
 
1501
 
 
1502
1216
    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
 
 
 
1217
        """
 
1218
        Actually parse the config file
 
1219
        
 
1220
        Testing Interpolation
 
1221
        
 
1222
        >>> c = ConfigObj()
 
1223
        >>> c['DEFAULT'] = {
 
1224
        ...     'b': 'goodbye',
 
1225
        ...     'userdir': 'c:\\\\home',
 
1226
        ...     'c': '%(d)s',
 
1227
        ...     'd': '%(c)s'
 
1228
        ... }
 
1229
        >>> c['section'] = {
 
1230
        ...     'a': '%(datadir)s\\\\some path\\\\file.py',
 
1231
        ...     'b': '%(userdir)s\\\\some path\\\\file.py',
 
1232
        ...     'c': 'Yo %(a)s',
 
1233
        ...     'd': '%(not_here)s',
 
1234
        ...     'e': '%(c)s',
 
1235
        ... }
 
1236
        >>> c['section']['DEFAULT'] = {
 
1237
        ...     'datadir': 'c:\\\\silly_test',
 
1238
        ...     'a': 'hello - %(b)s',
 
1239
        ... }
 
1240
        >>> c['section']['a'] == 'c:\\\\silly_test\\\\some path\\\\file.py'
 
1241
        1
 
1242
        >>> c['section']['b'] == 'c:\\\\home\\\\some path\\\\file.py'
 
1243
        1
 
1244
        >>> c['section']['c'] == 'Yo hello - goodbye'
 
1245
        1
 
1246
        
 
1247
        Switching Interpolation Off
 
1248
        
 
1249
        >>> c.interpolation = False
 
1250
        >>> c['section']['a'] == '%(datadir)s\\\\some path\\\\file.py'
 
1251
        1
 
1252
        >>> c['section']['b'] == '%(userdir)s\\\\some path\\\\file.py'
 
1253
        1
 
1254
        >>> c['section']['c'] == 'Yo %(a)s'
 
1255
        1
 
1256
        
 
1257
        Testing the interpolation errors.
 
1258
        
 
1259
        >>> c.interpolation = True
 
1260
        >>> c['section']['d']
 
1261
        Traceback (most recent call last):
 
1262
        MissingInterpolationOption: missing option "not_here" in interpolation.
 
1263
        >>> c['section']['e']
 
1264
        Traceback (most recent call last):
 
1265
        InterpolationDepthError: max interpolation depth exceeded in value "%(c)s".
 
1266
        
 
1267
        Testing our quoting.
 
1268
        
 
1269
        >>> i._quote('\"""\'\'\'')
 
1270
        Traceback (most recent call last):
 
1271
        SyntaxError: EOF while scanning triple-quoted string
 
1272
        >>> try:
 
1273
        ...     i._quote('\\n', multiline=False)
 
1274
        ... except ConfigObjError, e:
 
1275
        ...    e.msg
 
1276
        'Value "\\n" cannot be safely quoted.'
 
1277
        >>> k._quote(' "\' ', multiline=False)
 
1278
        Traceback (most recent call last):
 
1279
        SyntaxError: EOL while scanning single-quoted string
 
1280
        
 
1281
        Testing with "stringify" off.
 
1282
        >>> c.stringify = False
 
1283
        >>> c['test'] = 1
 
1284
        Traceback (most recent call last):
 
1285
        TypeError: Value is not a string "1".
 
1286
        """
1508
1287
        comment_list = []
1509
1288
        done_start = False
1510
1289
        this_section = self
1511
1290
        maxline = len(infile) - 1
1512
1291
        cur_index = -1
1513
1292
        reset_comment = False
1514
 
 
1515
1293
        while cur_index < maxline:
1516
1294
            if reset_comment:
1517
1295
                comment_list = []
1523
1301
                reset_comment = False
1524
1302
                comment_list.append(line)
1525
1303
                continue
1526
 
 
1527
1304
            if not done_start:
1528
1305
                # preserve initial comment
1529
1306
                self.initial_comment = comment_list
1530
1307
                comment_list = []
1531
1308
                done_start = True
1532
 
 
1533
1309
            reset_comment = True
1534
1310
            # first we check if it's a section marker
1535
1311
            mat = self._sectionmarker.match(line)
 
1312
##            sys.stderr.write('%s %s\n' % (sline, mat))
1536
1313
            if mat is not None:
1537
1314
                # is a section line
1538
 
                (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
 
1315
                (indent, sect_open, sect_name, sect_close, comment) = (
 
1316
                    mat.groups())
1539
1317
                if indent and (self.indent_type is None):
1540
 
                    self.indent_type = indent
 
1318
                    self.indent_type = indent[0]
1541
1319
                cur_depth = sect_open.count('[')
1542
1320
                if cur_depth != sect_close.count(']'):
1543
 
                    self._handle_error("Cannot compute the section depth at line %s.",
1544
 
                                       NestingError, infile, cur_index)
 
1321
                    self._handle_error(
 
1322
                        "Cannot compute the section depth at line %s.",
 
1323
                        NestingError, infile, cur_index)
1545
1324
                    continue
1546
 
 
1547
1325
                if cur_depth < this_section.depth:
1548
1326
                    # the new section is dropping back to a previous level
1549
1327
                    try:
1550
 
                        parent = self._match_depth(this_section,
1551
 
                                                   cur_depth).parent
 
1328
                        parent = self._match_depth(
 
1329
                            this_section,
 
1330
                            cur_depth).parent
1552
1331
                    except SyntaxError:
1553
 
                        self._handle_error("Cannot compute nesting level at line %s.",
1554
 
                                           NestingError, infile, cur_index)
 
1332
                        self._handle_error(
 
1333
                            "Cannot compute nesting level at line %s.",
 
1334
                            NestingError, infile, cur_index)
1555
1335
                        continue
1556
1336
                elif cur_depth == this_section.depth:
1557
1337
                    # the new section is a sibling of the current section
1560
1340
                    # the new section is a child the current section
1561
1341
                    parent = this_section
1562
1342
                else:
1563
 
                    self._handle_error("Section too nested at line %s.",
1564
 
                                       NestingError, infile, cur_index)
1565
 
 
 
1343
                    self._handle_error(
 
1344
                        "Section too nested at line %s.",
 
1345
                        NestingError, infile, cur_index)
 
1346
                #
1566
1347
                sect_name = self._unquote(sect_name)
1567
1348
                if sect_name in parent:
1568
 
                    self._handle_error('Duplicate section name at line %s.',
1569
 
                                       DuplicateError, infile, cur_index)
 
1349
##                    sys.stderr.write(sect_name)
 
1350
##                    sys.stderr.write('\n')
 
1351
                    self._handle_error(
 
1352
                        'Duplicate section name at line %s.',
 
1353
                        DuplicateError, infile, cur_index)
1570
1354
                    continue
1571
 
 
1572
1355
                # create the new section
1573
1356
                this_section = Section(
1574
1357
                    parent,
1578
1361
                parent[sect_name] = this_section
1579
1362
                parent.inline_comments[sect_name] = comment
1580
1363
                parent.comments[sect_name] = comment_list
 
1364
##                sys.stderr.write(parent[sect_name] is this_section)
 
1365
##                sys.stderr.write('\n')
1581
1366
                continue
1582
1367
            #
1583
1368
            # it's not a section marker,
1584
1369
            # so it should be a valid ``key = value`` line
1585
1370
            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:
 
1371
##            sys.stderr.write('%s %s\n' % (sline, mat))
 
1372
            if mat is not None:
1593
1373
                # is a keyword value
1594
1374
                # value will include any inline comment
1595
1375
                (indent, key, value) = mat.groups()
1596
1376
                if indent and (self.indent_type is None):
1597
 
                    self.indent_type = indent
 
1377
                    self.indent_type = indent[0]
1598
1378
                # check for a multiline value
1599
1379
                if value[:3] in ['"""', "'''"]:
1600
1380
                    try:
1605
1385
                            'Parse error in value at line %s.',
1606
1386
                            ParseError, infile, cur_index)
1607
1387
                        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
1388
                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
 
1389
                    # extract comment and lists
 
1390
                    try:
 
1391
                        (value, comment) = self._handle_value(value)
 
1392
                    except SyntaxError:
 
1393
                        self._handle_error(
 
1394
                            'Parse error in value at line %s.',
 
1395
                            ParseError, infile, cur_index)
 
1396
                        continue
1643
1397
                #
 
1398
##                sys.stderr.write(sline)
 
1399
##                sys.stderr.write('\n')
1644
1400
                key = self._unquote(key)
1645
1401
                if key in this_section:
1646
1402
                    self._handle_error(
1647
1403
                        'Duplicate keyword name at line %s.',
1648
1404
                        DuplicateError, infile, cur_index)
1649
1405
                    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)
 
1406
                # add the key
 
1407
##                sys.stderr.write(this_section.name + '\n')
 
1408
                this_section[key] = value
1654
1409
                this_section.inline_comments[key] = comment
1655
1410
                this_section.comments[key] = comment_list
 
1411
##                sys.stderr.write('%s %s\n' % (key, this_section[key]))
 
1412
##                if this_section.name is not None:
 
1413
##                    sys.stderr.write(this_section + '\n')
 
1414
##                    sys.stderr.write(this_section.parent + '\n')
 
1415
##                    sys.stderr.write(this_section.parent[this_section.name])
 
1416
##                    sys.stderr.write('\n')
1656
1417
                continue
1657
 
        #
 
1418
            #
 
1419
            # it neither matched as a keyword
 
1420
            # or a section marker
 
1421
            self._handle_error(
 
1422
                'Invalid line at line "%s".',
 
1423
                ParseError, infile, cur_index)
1658
1424
        if self.indent_type is None:
1659
1425
            # no indentation used, set the type accordingly
1660
1426
            self.indent_type = ''
1661
 
 
1662
1427
        # preserve the final comment
1663
1428
        if not self and not self.initial_comment:
1664
1429
            self.initial_comment = comment_list
1665
 
        elif not reset_comment:
 
1430
        else:
1666
1431
            self.final_comment = comment_list
1667
 
        self.list_values = temp_list_values
1668
 
 
1669
1432
 
1670
1433
    def _match_depth(self, sect, depth):
1671
1434
        """
1672
1435
        Given a section and a depth level, walk back through the sections
1673
1436
        parents to see if the depth level matches a previous section.
1674
 
 
 
1437
        
1675
1438
        Return a reference to the right section,
1676
1439
        or raise a SyntaxError.
1677
1440
        """
1678
1441
        while depth < sect.depth:
1679
1442
            if sect is sect.parent:
1680
1443
                # we've reached the top level already
1681
 
                raise SyntaxError()
 
1444
                raise SyntaxError
1682
1445
            sect = sect.parent
1683
1446
        if sect.depth == depth:
1684
1447
            return sect
1685
1448
        # shouldn't get here
1686
 
        raise SyntaxError()
1687
 
 
 
1449
        raise SyntaxError
1688
1450
 
1689
1451
    def _handle_error(self, text, ErrorClass, infile, cur_index):
1690
1452
        """
1691
1453
        Handle an error according to the error settings.
1692
 
 
 
1454
        
1693
1455
        Either raise the error or store it.
1694
1456
        The error will have occured at ``cur_index``
1695
1457
        """
1704
1466
        # reraise when parsing has finished
1705
1467
        self._errors.append(error)
1706
1468
 
1707
 
 
1708
1469
    def _unquote(self, value):
1709
1470
        """Return an unquoted version of a value"""
1710
1471
        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1711
1472
            value = value[1:-1]
1712
1473
        return value
1713
1474
 
1714
 
 
1715
1475
    def _quote(self, value, multiline=True):
1716
1476
        """
1717
1477
        Return a safely quoted version of a value.
1718
 
 
 
1478
        
1719
1479
        Raise a ConfigObjError if the value cannot be safely quoted.
1720
1480
        If multiline is ``True`` (default) then use triple quotes
1721
1481
        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
 
 
 
1482
        
 
1483
        Don't quote values that don't need it.
 
1484
        Recursively quote members of a list and return a comma joined list.
 
1485
        Multiline is ``False`` for lists.
 
1486
        Obey list syntax for empty and single member lists.
 
1487
        
1728
1488
        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.
 
1489
        a ``\n`` (is multiline).
1733
1490
        """
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)):
 
1491
        if isinstance(value, (list, tuple)):
1740
1492
            if not value:
1741
1493
                return ','
1742
1494
            elif len(value) == 1:
1743
1495
                return self._quote(value[0], multiline=False) + ','
1744
1496
            return ', '.join([self._quote(val, multiline=False)
1745
1497
                for val in value])
1746
 
        if not isinstance(value, basestring):
 
1498
        if not isinstance(value, StringTypes):
1747
1499
            if self.stringify:
1748
1500
                value = str(value)
1749
1501
            else:
1750
 
                raise TypeError('Value "%s" is not a string.' % value)
1751
 
 
 
1502
                raise TypeError, 'Value "%s" is not a string.' % value
 
1503
        squot = "'%s'"
 
1504
        dquot = '"%s"'
 
1505
        noquot = "%s"
 
1506
        wspace_plus = ' \r\t\n\v\t\'"'
 
1507
        tsquot = '"""%s"""'
 
1508
        tdquot = "'''%s'''"
1752
1509
        if not value:
1753
1510
            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:
 
1511
        if (not self.list_values and '\n' not in value) or not (multiline and
 
1512
                ((("'" in value) and ('"' in value)) or ('\n' in value))):
1761
1513
            if not self.list_values:
1762
1514
                # we don't quote if ``list_values=False``
1763
1515
                quot = noquot
1764
1516
            # for normal values either single or double quotes will do
1765
1517
            elif '\n' in value:
1766
1518
                # will only happen if multiline is off - e.g. '\n' in key
1767
 
                raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
 
1519
                raise ConfigObjError, ('Value "%s" cannot be safely quoted.' %
 
1520
                    value)
1768
1521
            elif ((value[0] not in wspace_plus) and
1769
1522
                    (value[-1] not in wspace_plus) and
1770
1523
                    (',' not in value)):
1771
1524
                quot = noquot
1772
1525
            else:
1773
 
                quot = self._get_single_quote(value)
 
1526
                if ("'" in value) and ('"' in value):
 
1527
                    raise ConfigObjError, (
 
1528
                        'Value "%s" cannot be safely quoted.' % value)
 
1529
                elif '"' in value:
 
1530
                    quot = squot
 
1531
                else:
 
1532
                    quot = dquot
1774
1533
        else:
1775
1534
            # 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
 
 
 
1535
            if (value.find('"""') != -1) and (value.find("'''") != -1):
 
1536
                raise ConfigObjError, (
 
1537
                    'Value "%s" cannot be safely quoted.' % value)
 
1538
            if value.find('"""') == -1:
 
1539
                quot = tdquot
 
1540
            else:
 
1541
                quot = tsquot
1781
1542
        return quot % value
1782
1543
 
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
1544
    def _handle_value(self, value):
1805
1545
        """
1806
1546
        Given a value string, unquote, remove comment,
1807
1547
        handle lists. (including empty and single member lists)
 
1548
        
 
1549
        Testing list values.
 
1550
        
 
1551
        >>> testconfig3 = '''
 
1552
        ... a = ,
 
1553
        ... b = test,
 
1554
        ... c = test1, test2   , test3
 
1555
        ... d = test1, test2, test3,
 
1556
        ... '''
 
1557
        >>> d = ConfigObj(testconfig3.split('\\n'), raise_errors=True)
 
1558
        >>> d['a'] == []
 
1559
        1
 
1560
        >>> d['b'] == ['test']
 
1561
        1
 
1562
        >>> d['c'] == ['test1', 'test2', 'test3']
 
1563
        1
 
1564
        >>> d['d'] == ['test1', 'test2', 'test3']
 
1565
        1
 
1566
        
 
1567
        Testing with list values off.
 
1568
        
 
1569
        >>> e = ConfigObj(
 
1570
        ...     testconfig3.split('\\n'),
 
1571
        ...     raise_errors=True,
 
1572
        ...     list_values=False)
 
1573
        >>> e['a'] == ','
 
1574
        1
 
1575
        >>> e['b'] == 'test,'
 
1576
        1
 
1577
        >>> e['c'] == 'test1, test2   , test3'
 
1578
        1
 
1579
        >>> e['d'] == 'test1, test2, test3,'
 
1580
        1
 
1581
        
 
1582
        Testing creating from a dictionary.
 
1583
        
 
1584
        >>> f = {
 
1585
        ...     'key1': 'val1',
 
1586
        ...     'key2': 'val2',
 
1587
        ...     'section 1': {
 
1588
        ...         'key1': 'val1',
 
1589
        ...         'key2': 'val2',
 
1590
        ...         'section 1b': {
 
1591
        ...             'key1': 'val1',
 
1592
        ...             'key2': 'val2',
 
1593
        ...         },
 
1594
        ...     },
 
1595
        ...     'section 2': {
 
1596
        ...         'key1': 'val1',
 
1597
        ...         'key2': 'val2',
 
1598
        ...         'section 2b': {
 
1599
        ...             'key1': 'val1',
 
1600
        ...             'key2': 'val2',
 
1601
        ...         },
 
1602
        ...     },
 
1603
        ...      'key3': 'val3',
 
1604
        ... }
 
1605
        >>> g = ConfigObj(f)
 
1606
        >>> f == g
 
1607
        1
 
1608
        
 
1609
        Testing we correctly detect badly built list values (4 of them).
 
1610
        
 
1611
        >>> testconfig4 = '''
 
1612
        ... config = 3,4,,
 
1613
        ... test = 3,,4
 
1614
        ... fish = ,,
 
1615
        ... dummy = ,,hello, goodbye
 
1616
        ... '''
 
1617
        >>> try:
 
1618
        ...     ConfigObj(testconfig4.split('\\n'))
 
1619
        ... except ConfigObjError, e:
 
1620
        ...     len(e.errors)
 
1621
        4
 
1622
        
 
1623
        Testing we correctly detect badly quoted values (4 of them).
 
1624
        
 
1625
        >>> testconfig5 = '''
 
1626
        ... config = "hello   # comment
 
1627
        ... test = 'goodbye
 
1628
        ... fish = 'goodbye   # comment
 
1629
        ... dummy = "hello again
 
1630
        ... '''
 
1631
        >>> try:
 
1632
        ...     ConfigObj(testconfig5.split('\\n'))
 
1633
        ... except ConfigObjError, e:
 
1634
        ...     len(e.errors)
 
1635
        4
1808
1636
        """
1809
 
        if self._inspec:
1810
 
            # Parsing a configspec so don't handle comments
1811
 
            return (value, '')
1812
1637
        # do we look for lists in values ?
1813
1638
        if not self.list_values:
1814
1639
            mat = self._nolistvalue.match(value)
1815
1640
            if mat is None:
1816
 
                raise SyntaxError()
 
1641
                raise SyntaxError
 
1642
            (value, comment) = mat.groups()
1817
1643
            # NOTE: we don't unquote here
1818
 
            return mat.groups()
1819
 
        #
 
1644
            return (value, comment)
1820
1645
        mat = self._valueexp.match(value)
1821
1646
        if mat is None:
1822
1647
            # the value is badly constructed, probably badly quoted,
1823
1648
            # or an invalid list
1824
 
            raise SyntaxError()
 
1649
            raise SyntaxError
1825
1650
        (list_values, single, empty_list, comment) = mat.groups()
1826
1651
        if (list_values == '') and (single is None):
1827
1652
            # change this if you want to accept empty values
1828
 
            raise SyntaxError()
 
1653
            raise SyntaxError
1829
1654
        # NOTE: note there is no error handling from here if the regex
1830
1655
        # is wrong: then incorrect values will slip through
1831
1656
        if empty_list is not None:
1832
1657
            # the single comma - meaning an empty list
1833
1658
            return ([], comment)
1834
1659
        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)
 
1660
            single = self._unquote(single)
1843
1661
        if list_values == '':
1844
1662
            # not a list value
1845
1663
            return (single, comment)
1849
1667
            the_list += [single]
1850
1668
        return (the_list, comment)
1851
1669
 
1852
 
 
1853
1670
    def _multiline(self, value, infile, cur_index, maxline):
1854
 
        """Extract the value, where we are in a multiline situation."""
 
1671
        """
 
1672
        Extract the value, where we are in a multiline situation
 
1673
        
 
1674
        Testing multiline values.
 
1675
        
 
1676
        >>> i == {
 
1677
        ...     'name4': ' another single line value ',
 
1678
        ...     'multi section': {
 
1679
        ...         'name4': '\\n        Well, this is a\\n        multiline '
 
1680
        ...             'value\\n        ',
 
1681
        ...         'name2': '\\n        Well, this is a\\n        multiline '
 
1682
        ...             'value\\n        ',
 
1683
        ...         'name3': '\\n        Well, this is a\\n        multiline '
 
1684
        ...             'value\\n        ',
 
1685
        ...         'name1': '\\n        Well, this is a\\n        multiline '
 
1686
        ...             'value\\n        ',
 
1687
        ...     },
 
1688
        ...     'name2': ' another single line value ',
 
1689
        ...     'name3': ' a single line value ',
 
1690
        ...     'name1': ' a single line value ',
 
1691
        ... }
 
1692
        1
 
1693
        """
1855
1694
        quot = value[:3]
1856
1695
        newvalue = value[3:]
1857
1696
        single_line = self._triple_quote[quot][0]
1863
1702
            return retval
1864
1703
        elif newvalue.find(quot) != -1:
1865
1704
            # somehow the triple quote is missing
1866
 
            raise SyntaxError()
 
1705
            raise SyntaxError
1867
1706
        #
1868
1707
        while cur_index < maxline:
1869
1708
            cur_index += 1
1876
1715
                break
1877
1716
        else:
1878
1717
            # we've got to the end of the config, oops...
1879
 
            raise SyntaxError()
 
1718
            raise SyntaxError
1880
1719
        mat = multi_line.match(line)
1881
1720
        if mat is None:
1882
1721
            # a badly formed line
1883
 
            raise SyntaxError()
 
1722
            raise SyntaxError
1884
1723
        (value, comment) = mat.groups()
1885
1724
        return (newvalue + value, comment, cur_index)
1886
1725
 
1887
 
 
1888
1726
    def _handle_configspec(self, configspec):
1889
1727
        """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
 
 
 
1728
        try:
 
1729
            configspec = ConfigObj(
 
1730
                configspec,
 
1731
                raise_errors=True,
 
1732
                file_error=True,
 
1733
                list_values=False)
 
1734
        except ConfigObjError, e:
 
1735
            # FIXME: Should these errors have a reference
 
1736
            # to the already parsed ConfigObj ?
 
1737
            raise ConfigspecError('Parsing configspec failed: %s' % e)
 
1738
        except IOError, e:
 
1739
            raise IOError('Reading configspec failed: %s' % e)
 
1740
        self._set_configspec_value(configspec, self)
 
1741
 
 
1742
    def _set_configspec_value(self, configspec, section):
 
1743
        """Used to recursively set configspec values."""
 
1744
        if '__many__' in configspec.sections:
 
1745
            section.configspec['__many__'] = configspec['__many__']
 
1746
            if len(configspec.sections) > 1:
 
1747
                # FIXME: can we supply any useful information here ?
 
1748
                raise RepeatSectionError
 
1749
        for entry in configspec.scalars:
 
1750
            section.configspec[entry] = configspec[entry]
1921
1751
        for entry in configspec.sections:
1922
1752
            if entry == '__many__':
1923
1753
                continue
1924
1754
            if entry not in section:
1925
1755
                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
 
 
 
1756
            self._set_configspec_value(configspec[entry], section[entry])
 
1757
 
 
1758
    def _handle_repeat(self, section, configspec):
 
1759
        """Dynamically assign configspec for repeated section."""
 
1760
        try:
 
1761
            section_keys = configspec.sections
 
1762
            scalar_keys = configspec.scalars
 
1763
        except AttributeError:
 
1764
            section_keys = [entry for entry in configspec 
 
1765
                                if isinstance(configspec[entry], dict)]
 
1766
            scalar_keys = [entry for entry in configspec 
 
1767
                                if not isinstance(configspec[entry], dict)]
 
1768
        if '__many__' in section_keys and len(section_keys) > 1:
 
1769
            # FIXME: can we supply any useful information here ?
 
1770
            raise RepeatSectionError
 
1771
        scalars = {}
 
1772
        sections = {}
 
1773
        for entry in scalar_keys:
 
1774
            val = configspec[entry]
 
1775
            scalars[entry] = val
 
1776
        for entry in section_keys:
 
1777
            val = configspec[entry]
 
1778
            if entry == '__many__':
 
1779
                scalars[entry] = val
 
1780
                continue
 
1781
            sections[entry] = val
 
1782
        #
 
1783
        section.configspec = scalars
 
1784
        for entry in sections:
 
1785
            if entry not in section:
 
1786
                section[entry] = {}
 
1787
            self._handle_repeat(section[entry], sections[entry])
1935
1788
 
1936
1789
    def _write_line(self, indent_string, entry, this_entry, comment):
1937
1790
        """Write an individual line, for the write method"""
1938
1791
        # 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
 
 
 
1792
        return '%s%s%s%s%s' % (
 
1793
            indent_string,
 
1794
            self._decode_element(self._quote(entry, multiline=False)),
 
1795
            self._a_to_u(' = '),
 
1796
            self._decode_element(self._quote(this_entry)),
 
1797
            self._decode_element(comment))
1949
1798
 
1950
1799
    def _write_marker(self, indent_string, depth, entry, comment):
1951
1800
        """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
 
 
 
1801
        return '%s%s%s%s%s' % (
 
1802
            indent_string,
 
1803
            self._a_to_u('[' * depth),
 
1804
            self._quote(self._decode_element(entry), multiline=False),
 
1805
            self._a_to_u(']' * depth),
 
1806
            self._decode_element(comment))
1958
1807
 
1959
1808
    def _handle_comment(self, comment):
1960
 
        """Deal with a comment."""
 
1809
        """
 
1810
        Deal with a comment.
 
1811
        
 
1812
        >>> filename = a.filename
 
1813
        >>> a.filename = None
 
1814
        >>> values = a.write()
 
1815
        >>> index = 0
 
1816
        >>> while index < 23:
 
1817
        ...     index += 1
 
1818
        ...     line = values[index-1]
 
1819
        ...     assert line.endswith('# comment ' + str(index))
 
1820
        >>> a.filename = filename
 
1821
        
 
1822
        >>> start_comment = ['# Initial Comment', '', '#']
 
1823
        >>> end_comment = ['', '#', '# Final Comment']
 
1824
        >>> newconfig = start_comment + testconfig1.split('\\n') + end_comment
 
1825
        >>> nc = ConfigObj(newconfig)
 
1826
        >>> nc.initial_comment
 
1827
        ['# Initial Comment', '', '#']
 
1828
        >>> nc.final_comment
 
1829
        ['', '#', '# Final Comment']
 
1830
        >>> nc.initial_comment == start_comment
 
1831
        1
 
1832
        >>> nc.final_comment == end_comment
 
1833
        1
 
1834
        """
1961
1835
        if not comment:
1962
1836
            return ''
1963
 
        start = self.indent_type
 
1837
        if self.indent_type == '\t':
 
1838
            start = self._a_to_u('\t')
 
1839
        else:
 
1840
            start = self._a_to_u(' ' * NUM_INDENT_SPACES)
1964
1841
        if not comment.startswith('#'):
1965
 
            start += self._a_to_u(' # ')
 
1842
            start += _a_to_u('# ')
1966
1843
        return (start + comment)
1967
1844
 
 
1845
    def _compute_indent_string(self, depth):
 
1846
        """
 
1847
        Compute the indent string, according to current indent_type and depth
 
1848
        """
 
1849
        if self.indent_type == '':
 
1850
            # no indentation at all
 
1851
            return ''
 
1852
        if self.indent_type == '\t':
 
1853
            return '\t' * depth
 
1854
        if self.indent_type == ' ':
 
1855
            return ' ' * NUM_INDENT_SPACES * depth
 
1856
        raise SyntaxError
1968
1857
 
1969
1858
    # Public methods
1970
1859
 
1971
1860
    def write(self, outfile=None, section=None):
1972
1861
        """
1973
1862
        Write the current ConfigObj as a file
1974
 
 
 
1863
        
1975
1864
        tekNico: FIXME: use StringIO instead of real files
1976
 
 
 
1865
        
1977
1866
        >>> filename = a.filename
1978
1867
        >>> a.filename = 'test.ini'
1979
1868
        >>> a.write()
1980
1869
        >>> a.filename = filename
1981
1870
        >>> a == ConfigObj('test.ini', raise_errors=True)
1982
1871
        1
 
1872
        >>> os.remove('test.ini')
 
1873
        >>> b.filename = 'test.ini'
 
1874
        >>> b.write()
 
1875
        >>> b == ConfigObj('test.ini', raise_errors=True)
 
1876
        1
 
1877
        >>> os.remove('test.ini')
 
1878
        >>> i.filename = 'test.ini'
 
1879
        >>> i.write()
 
1880
        >>> i == ConfigObj('test.ini', raise_errors=True)
 
1881
        1
 
1882
        >>> os.remove('test.ini')
 
1883
        >>> a = ConfigObj()
 
1884
        >>> a['DEFAULT'] = {'a' : 'fish'}
 
1885
        >>> a['a'] = '%(a)s'
 
1886
        >>> a.write()
 
1887
        ['a = %(a)s', '[DEFAULT]', 'a = fish']
1983
1888
        """
1984
1889
        if self.indent_type is None:
1985
1890
            # this can be true if initialised from a dictionary
1986
1891
            self.indent_type = DEFAULT_INDENT_TYPE
1987
 
 
 
1892
        #
1988
1893
        out = []
1989
1894
        cs = self._a_to_u('#')
1990
1895
        csp = self._a_to_u('# ')
1998
1903
                if stripped_line and not stripped_line.startswith(cs):
1999
1904
                    line = csp + line
2000
1905
                out.append(line)
2001
 
 
2002
 
        indent_string = self.indent_type * section.depth
 
1906
        #
 
1907
        indent_string = self._a_to_u(
 
1908
            self._compute_indent_string(section.depth))
2003
1909
        for entry in (section.scalars + section.sections):
2004
1910
            if entry in section.defaults:
2005
1911
                # don't write out default values
2011
1917
                out.append(indent_string + comment_line)
2012
1918
            this_entry = section[entry]
2013
1919
            comment = self._handle_comment(section.inline_comments[entry])
2014
 
 
 
1920
            #
2015
1921
            if isinstance(this_entry, dict):
2016
1922
                # a section
2017
1923
                out.append(self._write_marker(
2026
1932
                    entry,
2027
1933
                    this_entry,
2028
1934
                    comment))
2029
 
 
 
1935
        #
2030
1936
        if section is self:
2031
1937
            for line in self.final_comment:
2032
1938
                line = self._decode_element(line)
2035
1941
                    line = csp + line
2036
1942
                out.append(line)
2037
1943
            self.interpolation = int_val
2038
 
 
 
1944
        #
2039
1945
        if section is not self:
2040
1946
            return out
2041
 
 
 
1947
        #
2042
1948
        if (self.filename is None) and (outfile is None):
2043
1949
            # output a list of lines
2044
1950
            # might need to encode
2052
1958
                    out.append('')
2053
1959
                out[0] = BOM_UTF8 + out[0]
2054
1960
            return out
2055
 
 
 
1961
        #
2056
1962
        # 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)
 
1963
        output = (self._a_to_u(self.newlines or os.linesep)
 
1964
            ).join(out)
2059
1965
        if self.encoding:
2060
1966
            output = output.encode(self.encoding)
2061
 
        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
 
1967
        if (self.BOM and ((self.encoding is None) or
 
1968
            (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2062
1969
            # Add the UTF8 BOM
2063
1970
            output = BOM_UTF8 + output
2064
 
 
2065
 
        if not output.endswith(newline):
2066
 
            output += newline
2067
1971
        if outfile is not None:
2068
1972
            outfile.write(output)
2069
1973
        else:
2070
 
            h = open(self.filename, 'wb')
 
1974
            h = open(self.filename, 'w')
2071
1975
            h.write(output)
2072
1976
            h.close()
2073
1977
 
2074
 
 
2075
 
    def validate(self, validator, preserve_errors=False, copy=False,
2076
 
                 section=None):
 
1978
    def validate(self, validator, preserve_errors=False, section=None):
2077
1979
        """
2078
1980
        Test the ConfigObj against a configspec.
2079
 
 
 
1981
        
2080
1982
        It uses the ``validator`` object from *validate.py*.
2081
 
 
 
1983
        
2082
1984
        To run ``validate`` on the current ConfigObj, call: ::
2083
 
 
 
1985
        
2084
1986
            test = config.validate(validator)
2085
 
 
 
1987
        
2086
1988
        (Normally having previously passed in the configspec when the ConfigObj
2087
1989
        was created - you can dynamically assign a dictionary of checks to the
2088
1990
        ``configspec`` attribute of a section though).
2089
 
 
 
1991
        
2090
1992
        It returns ``True`` if everything passes, or a dictionary of
2091
1993
        pass/fails (True/False). If every member of a subsection passes, it
2092
1994
        will just have the value ``True``. (It also returns ``False`` if all
2093
1995
        members fail).
2094
 
 
 
1996
        
2095
1997
        In addition, it converts the values from strings to their native
2096
1998
        types if their checks pass (and ``stringify`` is set).
2097
 
 
 
1999
        
2098
2000
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2099
2001
        of a marking a fail with a ``False``, it will preserve the actual
2100
2002
        exception object. This can contain info about the reason for failure.
2101
 
        For example the ``VdtValueTooSmallError`` indicates that the value
 
2003
        For example the ``VdtValueTooSmallError`` indeicates that the value
2102
2004
        supplied was too small. If a value (or section) is missing it will
2103
2005
        still be marked as ``False``.
2104
 
 
 
2006
        
2105
2007
        You must have the validate module to use ``preserve_errors=True``.
2106
 
 
 
2008
        
2107
2009
        You can then use the ``flatten_errors`` function to turn your nested
2108
2010
        results dictionary into a flattened list of failures - useful for
2109
2011
        displaying meaningful error messages.
 
2012
        
 
2013
        >>> try:
 
2014
        ...     from validate import Validator
 
2015
        ... except ImportError:
 
2016
        ...     sys.stderr.write('Cannot import the Validator object, skipping the related tests\n')
 
2017
        ... else:
 
2018
        ...     config = '''
 
2019
        ...     test1=40
 
2020
        ...     test2=hello
 
2021
        ...     test3=3
 
2022
        ...     test4=5.0
 
2023
        ...     [section]
 
2024
        ...         test1=40
 
2025
        ...         test2=hello
 
2026
        ...         test3=3
 
2027
        ...         test4=5.0
 
2028
        ...         [[sub section]]
 
2029
        ...             test1=40
 
2030
        ...             test2=hello
 
2031
        ...             test3=3
 
2032
        ...             test4=5.0
 
2033
        ... '''.split('\\n')
 
2034
        ...     configspec = '''
 
2035
        ...     test1= integer(30,50)
 
2036
        ...     test2= string
 
2037
        ...     test3=integer
 
2038
        ...     test4=float(6.0)
 
2039
        ...     [section ]
 
2040
        ...         test1=integer(30,50)
 
2041
        ...         test2=string
 
2042
        ...         test3=integer
 
2043
        ...         test4=float(6.0)
 
2044
        ...         [[sub section]]
 
2045
        ...             test1=integer(30,50)
 
2046
        ...             test2=string
 
2047
        ...             test3=integer
 
2048
        ...             test4=float(6.0)
 
2049
        ...     '''.split('\\n')
 
2050
        ...     val = Validator()
 
2051
        ...     c1 = ConfigObj(config, configspec=configspec)
 
2052
        ...     test = c1.validate(val)
 
2053
        ...     test == {
 
2054
        ...         'test1': True,
 
2055
        ...         'test2': True,
 
2056
        ...         'test3': True,
 
2057
        ...         'test4': False,
 
2058
        ...         'section': {
 
2059
        ...             'test1': True,
 
2060
        ...             'test2': True,
 
2061
        ...             'test3': True,
 
2062
        ...             'test4': False,
 
2063
        ...             'sub section': {
 
2064
        ...                 'test1': True,
 
2065
        ...                 'test2': True,
 
2066
        ...                 'test3': True,
 
2067
        ...                 'test4': False,
 
2068
        ...             },
 
2069
        ...         },
 
2070
        ...     }
 
2071
        1
 
2072
        >>> val.check(c1.configspec['test4'], c1['test4'])
 
2073
        Traceback (most recent call last):
 
2074
        VdtValueTooSmallError: the value "5.0" is too small.
 
2075
        
 
2076
        >>> val_test_config = '''
 
2077
        ...     key = 0
 
2078
        ...     key2 = 1.1
 
2079
        ...     [section]
 
2080
        ...     key = some text
 
2081
        ...     key2 = 1.1, 3.0, 17, 6.8
 
2082
        ...         [[sub-section]]
 
2083
        ...         key = option1
 
2084
        ...         key2 = True'''.split('\\n')
 
2085
        >>> val_test_configspec = '''
 
2086
        ...     key = integer
 
2087
        ...     key2 = float
 
2088
        ...     [section]
 
2089
        ...     key = string
 
2090
        ...     key2 = float_list(4)
 
2091
        ...        [[sub-section]]
 
2092
        ...        key = option(option1, option2)
 
2093
        ...        key2 = boolean'''.split('\\n')
 
2094
        >>> val_test = ConfigObj(val_test_config, configspec=val_test_configspec)
 
2095
        >>> val_test.validate(val)
 
2096
        1
 
2097
        >>> val_test['key'] = 'text not a digit'
 
2098
        >>> val_res = val_test.validate(val)
 
2099
        >>> val_res == {'key2': True, 'section': True, 'key': False}
 
2100
        1
 
2101
        >>> configspec = '''
 
2102
        ...     test1=integer(30,50, default=40)
 
2103
        ...     test2=string(default="hello")
 
2104
        ...     test3=integer(default=3)
 
2105
        ...     test4=float(6.0, default=6.0)
 
2106
        ...     [section ]
 
2107
        ...         test1=integer(30,50, default=40)
 
2108
        ...         test2=string(default="hello")
 
2109
        ...         test3=integer(default=3)
 
2110
        ...         test4=float(6.0, default=6.0)
 
2111
        ...         [[sub section]]
 
2112
        ...             test1=integer(30,50, default=40)
 
2113
        ...             test2=string(default="hello")
 
2114
        ...             test3=integer(default=3)
 
2115
        ...             test4=float(6.0, default=6.0)
 
2116
        ...     '''.split('\\n')
 
2117
        >>> default_test = ConfigObj(['test1=30'], configspec=configspec)
 
2118
        >>> default_test
 
2119
        {'test1': '30', 'section': {'sub section': {}}}
 
2120
        >>> default_test.validate(val)
 
2121
        1
 
2122
        >>> default_test == {
 
2123
        ...     'test1': 30,
 
2124
        ...     'test2': 'hello',
 
2125
        ...     'test3': 3,
 
2126
        ...     'test4': 6.0,
 
2127
        ...     'section': {
 
2128
        ...         'test1': 40,
 
2129
        ...         'test2': 'hello',
 
2130
        ...         'test3': 3,
 
2131
        ...         'test4': 6.0,
 
2132
        ...         'sub section': {
 
2133
        ...             'test1': 40,
 
2134
        ...             'test3': 3,
 
2135
        ...             'test2': 'hello',
 
2136
        ...             'test4': 6.0,
 
2137
        ...         },
 
2138
        ...     },
 
2139
        ... }
 
2140
        1
 
2141
        
 
2142
        Now testing with repeated sections : BIG TEST
 
2143
        
 
2144
        >>> repeated_1 = '''
 
2145
        ... [dogs]
 
2146
        ...     [[__many__]] # spec for a dog
 
2147
        ...         fleas = boolean(default=True)
 
2148
        ...         tail = option(long, short, default=long)
 
2149
        ...         name = string(default=rover)
 
2150
        ...         [[[__many__]]]  # spec for a puppy
 
2151
        ...             name = string(default="son of rover")
 
2152
        ...             age = float(default=0.0)
 
2153
        ... [cats]
 
2154
        ...     [[__many__]] # spec for a cat
 
2155
        ...         fleas = boolean(default=True)
 
2156
        ...         tail = option(long, short, default=short)
 
2157
        ...         name = string(default=pussy)
 
2158
        ...         [[[__many__]]] # spec for a kitten
 
2159
        ...             name = string(default="son of pussy")
 
2160
        ...             age = float(default=0.0)
 
2161
        ...         '''.split('\\n')
 
2162
        >>> repeated_2 = '''
 
2163
        ... [dogs]
 
2164
        ... 
 
2165
        ...     # blank dogs with puppies
 
2166
        ...     # should be filled in by the configspec
 
2167
        ...     [[dog1]]
 
2168
        ...         [[[puppy1]]]
 
2169
        ...         [[[puppy2]]]
 
2170
        ...         [[[puppy3]]]
 
2171
        ...     [[dog2]]
 
2172
        ...         [[[puppy1]]]
 
2173
        ...         [[[puppy2]]]
 
2174
        ...         [[[puppy3]]]
 
2175
        ...     [[dog3]]
 
2176
        ...         [[[puppy1]]]
 
2177
        ...         [[[puppy2]]]
 
2178
        ...         [[[puppy3]]]
 
2179
        ... [cats]
 
2180
        ... 
 
2181
        ...     # blank cats with kittens
 
2182
        ...     # should be filled in by the configspec
 
2183
        ...     [[cat1]]
 
2184
        ...         [[[kitten1]]]
 
2185
        ...         [[[kitten2]]]
 
2186
        ...         [[[kitten3]]]
 
2187
        ...     [[cat2]]
 
2188
        ...         [[[kitten1]]]
 
2189
        ...         [[[kitten2]]]
 
2190
        ...         [[[kitten3]]]
 
2191
        ...     [[cat3]]
 
2192
        ...         [[[kitten1]]]
 
2193
        ...         [[[kitten2]]]
 
2194
        ...         [[[kitten3]]]
 
2195
        ... '''.split('\\n')
 
2196
        >>> repeated_3 = '''
 
2197
        ... [dogs]
 
2198
        ... 
 
2199
        ...     [[dog1]]
 
2200
        ...     [[dog2]]
 
2201
        ...     [[dog3]]
 
2202
        ... [cats]
 
2203
        ... 
 
2204
        ...     [[cat1]]
 
2205
        ...     [[cat2]]
 
2206
        ...     [[cat3]]
 
2207
        ... '''.split('\\n')
 
2208
        >>> repeated_4 = '''
 
2209
        ... [__many__]
 
2210
        ... 
 
2211
        ...     name = string(default=Michael)
 
2212
        ...     age = float(default=0.0)
 
2213
        ...     sex = option(m, f, default=m)
 
2214
        ... '''.split('\\n')
 
2215
        >>> repeated_5 = '''
 
2216
        ... [cats]
 
2217
        ... [[__many__]]
 
2218
        ...     fleas = boolean(default=True)
 
2219
        ...     tail = option(long, short, default=short)
 
2220
        ...     name = string(default=pussy)
 
2221
        ...     [[[description]]]
 
2222
        ...         height = float(default=3.3)
 
2223
        ...         weight = float(default=6)
 
2224
        ...         [[[[coat]]]]
 
2225
        ...             fur = option(black, grey, brown, "tortoise shell", default=black)
 
2226
        ...             condition = integer(0,10, default=5)
 
2227
        ... '''.split('\\n')
 
2228
        >>> from validate import Validator
 
2229
        >>> val= Validator()
 
2230
        >>> repeater = ConfigObj(repeated_2, configspec=repeated_1)
 
2231
        >>> repeater.validate(val)
 
2232
        1
 
2233
        >>> repeater == {
 
2234
        ...     'dogs': {
 
2235
        ...         'dog1': {
 
2236
        ...             'fleas': True,
 
2237
        ...             'tail': 'long',
 
2238
        ...             'name': 'rover',
 
2239
        ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
 
2240
        ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
 
2241
        ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
 
2242
        ...         },
 
2243
        ...         'dog2': {
 
2244
        ...             'fleas': True,
 
2245
        ...             'tail': 'long',
 
2246
        ...             'name': 'rover',
 
2247
        ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
 
2248
        ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
 
2249
        ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
 
2250
        ...         },
 
2251
        ...         'dog3': {
 
2252
        ...             'fleas': True,
 
2253
        ...             'tail': 'long',
 
2254
        ...             'name': 'rover',
 
2255
        ...             'puppy1': {'name': 'son of rover', 'age': 0.0},
 
2256
        ...             'puppy2': {'name': 'son of rover', 'age': 0.0},
 
2257
        ...             'puppy3': {'name': 'son of rover', 'age': 0.0},
 
2258
        ...         },
 
2259
        ...     },
 
2260
        ...     'cats': {
 
2261
        ...         'cat1': {
 
2262
        ...             'fleas': True,
 
2263
        ...             'tail': 'short',
 
2264
        ...             'name': 'pussy',
 
2265
        ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
 
2266
        ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
 
2267
        ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
 
2268
        ...         },
 
2269
        ...         'cat2': {
 
2270
        ...             'fleas': True,
 
2271
        ...             'tail': 'short',
 
2272
        ...             'name': 'pussy',
 
2273
        ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
 
2274
        ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
 
2275
        ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
 
2276
        ...         },
 
2277
        ...         'cat3': {
 
2278
        ...             'fleas': True,
 
2279
        ...             'tail': 'short',
 
2280
        ...             'name': 'pussy',
 
2281
        ...             'kitten1': {'name': 'son of pussy', 'age': 0.0},
 
2282
        ...             'kitten2': {'name': 'son of pussy', 'age': 0.0},
 
2283
        ...             'kitten3': {'name': 'son of pussy', 'age': 0.0},
 
2284
        ...         },
 
2285
        ...     },
 
2286
        ... }
 
2287
        1
 
2288
        >>> repeater = ConfigObj(repeated_3, configspec=repeated_1)
 
2289
        >>> repeater.validate(val)
 
2290
        1
 
2291
        >>> repeater == {
 
2292
        ...     'cats': {
 
2293
        ...         'cat1': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
 
2294
        ...         'cat2': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
 
2295
        ...         'cat3': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
 
2296
        ...     },
 
2297
        ...     'dogs': {
 
2298
        ...         'dog1': {'fleas': True, 'tail': 'long', 'name': 'rover'},
 
2299
        ...         'dog2': {'fleas': True, 'tail': 'long', 'name': 'rover'},
 
2300
        ...         'dog3': {'fleas': True, 'tail': 'long', 'name': 'rover'},
 
2301
        ...     },
 
2302
        ... }
 
2303
        1
 
2304
        >>> repeater = ConfigObj(configspec=repeated_4)
 
2305
        >>> repeater['Michael'] = {}
 
2306
        >>> repeater.validate(val)
 
2307
        1
 
2308
        >>> repeater == {
 
2309
        ...     'Michael': {'age': 0.0, 'name': 'Michael', 'sex': 'm'},
 
2310
        ... }
 
2311
        1
 
2312
        >>> repeater = ConfigObj(repeated_3, configspec=repeated_5)
 
2313
        >>> repeater == {
 
2314
        ...     'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
 
2315
        ...     'cats': {'cat1': {}, 'cat2': {}, 'cat3': {}},
 
2316
        ... }
 
2317
        1
 
2318
        >>> repeater.validate(val)
 
2319
        1
 
2320
        >>> repeater == {
 
2321
        ...     'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
 
2322
        ...     'cats': {
 
2323
        ...         'cat1': {
 
2324
        ...             'fleas': True,
 
2325
        ...             'tail': 'short',
 
2326
        ...             'name': 'pussy',
 
2327
        ...             'description': {
 
2328
        ...                 'weight': 6.0,
 
2329
        ...                 'height': 3.2999999999999998,
 
2330
        ...                 'coat': {'fur': 'black', 'condition': 5},
 
2331
        ...             },
 
2332
        ...         },
 
2333
        ...         'cat2': {
 
2334
        ...             'fleas': True,
 
2335
        ...             'tail': 'short',
 
2336
        ...             'name': 'pussy',
 
2337
        ...             'description': {
 
2338
        ...                 'weight': 6.0,
 
2339
        ...                 'height': 3.2999999999999998,
 
2340
        ...                 'coat': {'fur': 'black', 'condition': 5},
 
2341
        ...             },
 
2342
        ...         },
 
2343
        ...         'cat3': {
 
2344
        ...             'fleas': True,
 
2345
        ...             'tail': 'short',
 
2346
        ...             'name': 'pussy',
 
2347
        ...             'description': {
 
2348
        ...                 'weight': 6.0,
 
2349
        ...                 'height': 3.2999999999999998,
 
2350
        ...                 'coat': {'fur': 'black', 'condition': 5},
 
2351
        ...             },
 
2352
        ...         },
 
2353
        ...     },
 
2354
        ... }
 
2355
        1
 
2356
        
 
2357
        Test that interpolation is preserved for validated string values.
 
2358
        Also check that interpolation works in configspecs.
 
2359
        >>> t = ConfigObj()
 
2360
        >>> t['DEFAULT'] = {}
 
2361
        >>> t['DEFAULT']['test'] = 'a'
 
2362
        >>> t['test'] = '%(test)s'
 
2363
        >>> t['test']
 
2364
        'a'
 
2365
        >>> v = Validator()
 
2366
        >>> t.configspec = {'test': 'string'}
 
2367
        >>> t.validate(v)
 
2368
        1
 
2369
        >>> t.interpolation = False
 
2370
        >>> t
 
2371
        {'test': '%(test)s', 'DEFAULT': {'test': 'a'}}
 
2372
        >>> specs = [
 
2373
        ...    'interpolated string  = string(default="fuzzy-%(man)s")',
 
2374
        ...    '[DEFAULT]',
 
2375
        ...    'man = wuzzy',
 
2376
        ...    ]
 
2377
        >>> c = ConfigObj(configspec=specs)
 
2378
        >>> c.validate(v)
 
2379
        1
 
2380
        >>> c['interpolated string']
 
2381
        'fuzzy-wuzzy'
 
2382
        
 
2383
        FIXME: Above tests will fail if we couldn't import Validator (the ones
 
2384
        that don't raise errors will produce different output and still fail as
 
2385
        tests)
2110
2386
        """
2111
2387
        if section is None:
2112
2388
            if self.configspec is None:
2113
 
                raise ValueError('No configspec supplied.')
 
2389
                raise ValueError, 'No configspec supplied.'
2114
2390
            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
 
 
 
2391
                if VdtMissingValue is None:
 
2392
                    raise ImportError('Missing validate module.')
2120
2393
            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):
 
2394
        #
 
2395
        spec_section = section.configspec
 
2396
        if '__many__' in section.configspec:
 
2397
            many = spec_section['__many__']
 
2398
            # dynamically assign the configspecs
 
2399
            # for the sections below
 
2400
            for entry in section.sections:
 
2401
                self._handle_repeat(section[entry], many)
 
2402
        #
 
2403
        out = {}
 
2404
        ret_true = True
 
2405
        ret_false = True
 
2406
        for entry in spec_section:
 
2407
            if entry == '__many__':
 
2408
                continue
 
2409
            if (not entry in section.scalars) or (entry in section.defaults):
 
2410
                # missing entries
 
2411
                # or entries from defaults
 
2412
                missing = True
 
2413
                val = None
 
2414
            else:
 
2415
                missing = False
 
2416
                val = section[entry]
2135
2417
            try:
2136
 
                check = validator.check(spec,
 
2418
                check = validator.check(spec_section[entry],
2137
2419
                                        val,
2138
2420
                                        missing=missing
2139
2421
                                        )
2140
2422
            except validator.baseErrorClass, e:
2141
 
                if not preserve_errors or isinstance(e, self._vdtMissingValue):
 
2423
                if not preserve_errors or isinstance(e, VdtMissingValue):
2142
2424
                    out[entry] = False
2143
2425
                else:
2144
2426
                    # preserve the error
2146
2428
                    ret_false = False
2147
2429
                ret_true = False
2148
2430
            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
2431
                ret_false = False
2165
2432
                out[entry] = True
2166
2433
                if self.stringify or missing:
2177
2444
                            check = self._str(check)
2178
2445
                    if (check != val) or missing:
2179
2446
                        section[entry] = check
2180
 
                if not copy and missing and entry not in section.defaults:
 
2447
                if missing and entry not in section.defaults:
2181
2448
                    section.defaults.append(entry)
2182
 
            return ret_true, ret_false
2183
 
 
2184
2449
        #
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.
 
2450
        # FIXME: Will this miss missing sections ?
2248
2451
        for entry in section.sections:
2249
 
            # FIXME: this means DEFAULT is not copied in copy mode
2250
2452
            if section is self and entry == 'DEFAULT':
2251
2453
                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])
 
2454
            check = self.validate(validator, preserve_errors=preserve_errors,
 
2455
                section=section[entry])
2258
2456
            out[entry] = check
2259
2457
            if check == False:
2260
2458
                ret_true = False
2268
2466
            return True
2269
2467
        elif ret_false:
2270
2468
            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
 
 
 
2469
        else:
 
2470
            return out
2310
2471
 
2311
2472
class SimpleVal(object):
2312
2473
    """
2313
2474
    A simple validator.
2314
2475
    Can be used to check that all members expected are present.
2315
 
 
 
2476
    
2316
2477
    To use it, provide a configspec with all your members in (the value given
2317
2478
    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2318
2479
    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2319
2480
    members are present, or a dictionary with True/False meaning
2320
2481
    present/missing. (Whole missing sections will be replaced with ``False``)
 
2482
    
 
2483
    >>> val = SimpleVal()
 
2484
    >>> config = '''
 
2485
    ... test1=40
 
2486
    ... test2=hello
 
2487
    ... test3=3
 
2488
    ... test4=5.0
 
2489
    ... [section]
 
2490
    ... test1=40
 
2491
    ... test2=hello
 
2492
    ... test3=3
 
2493
    ... test4=5.0
 
2494
    ...     [[sub section]]
 
2495
    ...     test1=40
 
2496
    ...     test2=hello
 
2497
    ...     test3=3
 
2498
    ...     test4=5.0
 
2499
    ... '''.split('\\n')
 
2500
    >>> configspec = '''
 
2501
    ... test1=''
 
2502
    ... test2=''
 
2503
    ... test3=''
 
2504
    ... test4=''
 
2505
    ... [section]
 
2506
    ... test1=''
 
2507
    ... test2=''
 
2508
    ... test3=''
 
2509
    ... test4=''
 
2510
    ...     [[sub section]]
 
2511
    ...     test1=''
 
2512
    ...     test2=''
 
2513
    ...     test3=''
 
2514
    ...     test4=''
 
2515
    ... '''.split('\\n')
 
2516
    >>> o = ConfigObj(config, configspec=configspec)
 
2517
    >>> o.validate(val)
 
2518
    1
 
2519
    >>> o = ConfigObj(configspec=configspec)
 
2520
    >>> o.validate(val)
 
2521
    0
2321
2522
    """
2322
 
 
 
2523
    
2323
2524
    def __init__(self):
2324
2525
        self.baseErrorClass = ConfigObjError
2325
 
 
 
2526
    
2326
2527
    def check(self, check, member, missing=False):
2327
2528
        """A dummy check method, always returns the value unchanged."""
2328
2529
        if missing:
2329
 
            raise self.baseErrorClass()
 
2530
            raise self.baseErrorClass
2330
2531
        return member
2331
2532
 
2332
 
 
2333
2533
# Check / processing functions for options
2334
2534
def flatten_errors(cfg, res, levels=None, results=None):
2335
2535
    """
2336
2536
    An example function that will turn a nested dictionary of results
2337
2537
    (as returned by ``ConfigObj.validate``) into a flat list.
2338
 
 
 
2538
    
2339
2539
    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2340
2540
    dictionary returned by ``validate``.
2341
 
 
 
2541
    
2342
2542
    (This is a recursive function, so you shouldn't use the ``levels`` or
2343
 
    ``results`` arguments - they are used by the function.)
2344
 
 
 
2543
    ``results`` arguments - they are used by the function.
 
2544
    
2345
2545
    Returns a list of keys that failed. Each member of the list is a tuple :
2346
 
 
2347
2546
    ::
2348
 
 
 
2547
    
2349
2548
        ([list of sections...], key, result)
2350
 
 
 
2549
    
2351
2550
    If ``validate`` was called with ``preserve_errors=False`` (the default)
2352
2551
    then ``result`` will always be ``False``.
2353
2552
 
2354
2553
    *list of sections* is a flattened list of sections that the key was found
2355
2554
    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
 
 
 
2555
    
 
2556
    If the section was missing then key will be ``None``.
 
2557
    
2360
2558
    If the value (or section) was missing then ``result`` will be ``False``.
2361
 
 
 
2559
    
2362
2560
    If ``validate`` was called with ``preserve_errors=True`` and a value
2363
2561
    was present, but failed the check, then ``result`` will be the exception
2364
2562
    object returned. You can use this as a string that describes the failure.
2365
 
 
 
2563
    
2366
2564
    For example *The value "3" is of the wrong type*.
2367
 
 
 
2565
    
 
2566
    # FIXME: is the ordering of the output arbitrary ?
2368
2567
    >>> import validate
2369
2568
    >>> vtor = validate.Validator()
2370
2569
    >>> my_ini = '''
2434
2633
        results = []
2435
2634
    if res is True:
2436
2635
        return results
2437
 
    if res is False or isinstance(res, Exception):
2438
 
        results.append((levels[:], None, res))
 
2636
    if res is False:
 
2637
        results.append((levels[:], None, False))
2439
2638
        if levels:
2440
2639
            levels.pop()
2441
2640
        return results
2456
2655
    return results
2457
2656
 
2458
2657
 
2459
 
"""*A programming language is a medium of expression.* - Paul Graham"""
 
2658
# FIXME: test error code for badly built multiline values
 
2659
# FIXME: test handling of StringIO
 
2660
# FIXME: test interpolation with writing
 
2661
 
 
2662
def _doctest():
 
2663
    """
 
2664
    Dummy function to hold some of the doctests.
 
2665
    
 
2666
    >>> a.depth
 
2667
    0
 
2668
    >>> a == {
 
2669
    ...     'key2': 'val',
 
2670
    ...     'key1': 'val',
 
2671
    ...     'lev1c': {
 
2672
    ...         'lev2c': {
 
2673
    ...             'lev3c': {
 
2674
    ...                 'key1': 'val',
 
2675
    ...             },
 
2676
    ...         },
 
2677
    ...     },
 
2678
    ...     'lev1b': {
 
2679
    ...         'key2': 'val',
 
2680
    ...         'key1': 'val',
 
2681
    ...         'lev2ba': {
 
2682
    ...             'key1': 'val',
 
2683
    ...         },
 
2684
    ...         'lev2bb': {
 
2685
    ...             'key1': 'val',
 
2686
    ...         },
 
2687
    ...     },
 
2688
    ...     'lev1a': {
 
2689
    ...         'key2': 'val',
 
2690
    ...         'key1': 'val',
 
2691
    ...     },
 
2692
    ... }
 
2693
    1
 
2694
    >>> b.depth
 
2695
    0
 
2696
    >>> b == {
 
2697
    ...     'key3': 'val3',
 
2698
    ...     'key2': 'val2',
 
2699
    ...     'key1': 'val1',
 
2700
    ...     'section 1': {
 
2701
    ...         'keys11': 'val1',
 
2702
    ...         'keys13': 'val3',
 
2703
    ...         'keys12': 'val2',
 
2704
    ...     },
 
2705
    ...     'section 2': {
 
2706
    ...         'section 2 sub 1': {
 
2707
    ...             'fish': '3',
 
2708
    ...     },
 
2709
    ...     'keys21': 'val1',
 
2710
    ...     'keys22': 'val2',
 
2711
    ...     'keys23': 'val3',
 
2712
    ...     },
 
2713
    ... }
 
2714
    1
 
2715
    >>> t = '''
 
2716
    ... 'a' = b # !"$%^&*(),::;'@~#= 33
 
2717
    ... "b" = b #= 6, 33
 
2718
    ... ''' .split('\\n')
 
2719
    >>> t2 = ConfigObj(t)
 
2720
    >>> assert t2 == {'a': 'b', 'b': 'b'}
 
2721
    >>> t2.inline_comments['b'] = ''
 
2722
    >>> del t2['a']
 
2723
    >>> assert t2.write() == ['','b = b', '']
 
2724
    
 
2725
    # Test ``list_values=False`` stuff
 
2726
    >>> c = '''
 
2727
    ...     key1 = no quotes
 
2728
    ...     key2 = 'single quotes'
 
2729
    ...     key3 = "double quotes"
 
2730
    ...     key4 = "list", 'with', several, "quotes"
 
2731
    ...     '''
 
2732
    >>> cfg = ConfigObj(c.splitlines(), list_values=False)
 
2733
    >>> cfg == {'key1': 'no quotes', 'key2': "'single quotes'", 
 
2734
    ... 'key3': '"double quotes"', 
 
2735
    ... 'key4': '"list", \\'with\\', several, "quotes"'
 
2736
    ... }
 
2737
    1
 
2738
    >>> cfg = ConfigObj(list_values=False)
 
2739
    >>> cfg['key1'] = 'Multiline\\nValue'
 
2740
    >>> cfg['key2'] = '''"Value" with 'quotes' !'''
 
2741
    >>> cfg.write()
 
2742
    ["key1 = '''Multiline\\nValue'''", 'key2 = "Value" with \\'quotes\\' !']
 
2743
    >>> cfg.list_values = True
 
2744
    >>> cfg.write() == ["key1 = '''Multiline\\nValue'''",
 
2745
    ... 'key2 = \\'\\'\\'"Value" with \\'quotes\\' !\\'\\'\\'']
 
2746
    1
 
2747
    
 
2748
    Test flatten_errors:
 
2749
    
 
2750
    >>> from validate import Validator, VdtValueTooSmallError
 
2751
    >>> config = '''
 
2752
    ...     test1=40
 
2753
    ...     test2=hello
 
2754
    ...     test3=3
 
2755
    ...     test4=5.0
 
2756
    ...     [section]
 
2757
    ...         test1=40
 
2758
    ...         test2=hello
 
2759
    ...         test3=3
 
2760
    ...         test4=5.0
 
2761
    ...         [[sub section]]
 
2762
    ...             test1=40
 
2763
    ...             test2=hello
 
2764
    ...             test3=3
 
2765
    ...             test4=5.0
 
2766
    ... '''.split('\\n')
 
2767
    >>> configspec = '''
 
2768
    ...     test1= integer(30,50)
 
2769
    ...     test2= string
 
2770
    ...     test3=integer
 
2771
    ...     test4=float(6.0)
 
2772
    ...     [section ]
 
2773
    ...         test1=integer(30,50)
 
2774
    ...         test2=string
 
2775
    ...         test3=integer
 
2776
    ...         test4=float(6.0)
 
2777
    ...         [[sub section]]
 
2778
    ...             test1=integer(30,50)
 
2779
    ...             test2=string
 
2780
    ...             test3=integer
 
2781
    ...             test4=float(6.0)
 
2782
    ...     '''.split('\\n')
 
2783
    >>> val = Validator()
 
2784
    >>> c1 = ConfigObj(config, configspec=configspec)
 
2785
    >>> res = c1.validate(val)
 
2786
    >>> flatten_errors(c1, res) == [([], 'test4', False), (['section', 
 
2787
    ...     'sub section'], 'test4', False), (['section'], 'test4', False)]
 
2788
    True
 
2789
    >>> res = c1.validate(val, preserve_errors=True)
 
2790
    >>> check = flatten_errors(c1, res)
 
2791
    >>> check[0][:2]
 
2792
    ([], 'test4')
 
2793
    >>> check[1][:2]
 
2794
    (['section', 'sub section'], 'test4')
 
2795
    >>> check[2][:2]
 
2796
    (['section'], 'test4')
 
2797
    >>> for entry in check:
 
2798
    ...     isinstance(entry[2], VdtValueTooSmallError)
 
2799
    ...     print str(entry[2])
 
2800
    True
 
2801
    the value "5.0" is too small.
 
2802
    True
 
2803
    the value "5.0" is too small.
 
2804
    True
 
2805
    the value "5.0" is too small.
 
2806
    
 
2807
    Test unicode handling, BOM, write witha file like object and line endings :
 
2808
    >>> u_base = '''
 
2809
    ... # initial comment
 
2810
    ...     # inital comment 2
 
2811
    ... 
 
2812
    ... test1 = some value
 
2813
    ... # comment
 
2814
    ... test2 = another value    # inline comment
 
2815
    ... # section comment
 
2816
    ... [section]    # inline comment
 
2817
    ...     test = test    # another inline comment
 
2818
    ...     test2 = test2
 
2819
    ... 
 
2820
    ... # final comment
 
2821
    ... # final comment2
 
2822
    ... '''
 
2823
    >>> u = u_base.encode('utf_8').splitlines(True)
 
2824
    >>> u[0] = BOM_UTF8 + u[0]
 
2825
    >>> uc = ConfigObj(u)
 
2826
    >>> uc.encoding = None
 
2827
    >>> uc.BOM == True
 
2828
    1
 
2829
    >>> uc == {'test1': 'some value', 'test2': 'another value',
 
2830
    ... 'section': {'test': 'test', 'test2': 'test2'}}
 
2831
    1
 
2832
    >>> uc = ConfigObj(u, encoding='utf_8', default_encoding='latin-1')
 
2833
    >>> uc.BOM
 
2834
    1
 
2835
    >>> isinstance(uc['test1'], unicode)
 
2836
    1
 
2837
    >>> uc.encoding
 
2838
    'utf_8'
 
2839
    >>> uc.newlines
 
2840
    '\\n'
 
2841
    >>> uc['latin1'] = "This costs lot's of "
 
2842
    >>> a_list = uc.write()
 
2843
    >>> len(a_list)
 
2844
    15
 
2845
    >>> isinstance(a_list[0], str)
 
2846
    1
 
2847
    >>> a_list[0].startswith(BOM_UTF8)
 
2848
    1
 
2849
    >>> u = u_base.replace('\\n', '\\r\\n').encode('utf_8').splitlines(True)
 
2850
    >>> uc = ConfigObj(u)
 
2851
    >>> uc.newlines
 
2852
    '\\r\\n'
 
2853
    >>> uc.newlines = '\\r'
 
2854
    >>> from cStringIO import StringIO
 
2855
    >>> file_like = StringIO()
 
2856
    >>> uc.write(file_like)
 
2857
    >>> file_like.seek(0)
 
2858
    >>> uc2 = ConfigObj(file_like)
 
2859
    >>> uc2 == uc
 
2860
    1
 
2861
    >>> uc2.filename is None
 
2862
    1
 
2863
    >>> uc2.newlines == '\\r'
 
2864
    1
 
2865
    """
 
2866
 
 
2867
if __name__ == '__main__':
 
2868
    # run the code tests in doctest format
 
2869
    #
 
2870
    testconfig1 = """\
 
2871
    key1= val    # comment 1
 
2872
    key2= val    # comment 2
 
2873
    # comment 3
 
2874
    [lev1a]     # comment 4
 
2875
    key1= val    # comment 5
 
2876
    key2= val    # comment 6
 
2877
    # comment 7
 
2878
    [lev1b]    # comment 8
 
2879
    key1= val    # comment 9
 
2880
    key2= val    # comment 10
 
2881
    # comment 11
 
2882
        [[lev2ba]]    # comment 12
 
2883
        key1= val    # comment 13
 
2884
        # comment 14
 
2885
        [[lev2bb]]    # comment 15
 
2886
        key1= val    # comment 16
 
2887
    # comment 17
 
2888
    [lev1c]    # comment 18
 
2889
    # comment 19
 
2890
        [[lev2c]]    # comment 20
 
2891
        # comment 21
 
2892
            [[[lev3c]]]    # comment 22
 
2893
            key1 = val    # comment 23"""
 
2894
    #
 
2895
    testconfig2 = """\
 
2896
                        key1 = 'val1'
 
2897
                        key2 =   "val2"
 
2898
                        key3 = val3
 
2899
                        ["section 1"] # comment
 
2900
                        keys11 = val1
 
2901
                        keys12 = val2
 
2902
                        keys13 = val3
 
2903
                        [section 2]
 
2904
                        keys21 = val1
 
2905
                        keys22 = val2
 
2906
                        keys23 = val3
 
2907
                        
 
2908
                            [['section 2 sub 1']]
 
2909
                            fish = 3
 
2910
    """
 
2911
    #
 
2912
    testconfig6 = '''
 
2913
    name1 = """ a single line value """ # comment
 
2914
    name2 = \''' another single line value \''' # comment
 
2915
    name3 = """ a single line value """
 
2916
    name4 = \''' another single line value \'''
 
2917
        [ "multi section" ]
 
2918
        name1 = """
 
2919
        Well, this is a
 
2920
        multiline value
 
2921
        """
 
2922
        name2 = \'''
 
2923
        Well, this is a
 
2924
        multiline value
 
2925
        \'''
 
2926
        name3 = """
 
2927
        Well, this is a
 
2928
        multiline value
 
2929
        """     # a comment
 
2930
        name4 = \'''
 
2931
        Well, this is a
 
2932
        multiline value
 
2933
        \'''  # I guess this is a comment too
 
2934
    '''
 
2935
    #
 
2936
    import doctest
 
2937
    m = sys.modules.get('__main__')
 
2938
    globs = m.__dict__.copy()
 
2939
    a = ConfigObj(testconfig1.split('\n'), raise_errors=True)
 
2940
    b = ConfigObj(testconfig2.split('\n'), raise_errors=True)
 
2941
    i = ConfigObj(testconfig6.split('\n'), raise_errors=True)
 
2942
    globs.update({
 
2943
        'INTP_VER': INTP_VER,
 
2944
        'a': a,
 
2945
        'b': b,
 
2946
        'i': i,
 
2947
    })
 
2948
    doctest.testmod(m, globs=globs)
 
2949
 
 
2950
"""
 
2951
    BUGS
 
2952
    ====
 
2953
    
 
2954
    None known.
 
2955
    
 
2956
    TODO
 
2957
    ====
 
2958
    
 
2959
    Better support for configuration from multiple files, including tracking
 
2960
    *where* the original file came from and writing changes to the correct
 
2961
    file.
 
2962
    
 
2963
    
 
2964
    Make ``newline`` an option (as well as an attribute) ?
 
2965
    
 
2966
    ``UTF16`` encoded files, when returned as a list of lines, will have the
 
2967
    BOM at the start of every line. Should this be removed from all but the
 
2968
    first line ?
 
2969
    
 
2970
    Option to set warning type for unicode decode ? (Defaults to strict).
 
2971
    
 
2972
    A method to optionally remove uniform indentation from multiline values.
 
2973
    (do as an example of using ``walk`` - along with string-escape)
 
2974
    
 
2975
    Should the results dictionary from validate be an ordered dictionary if
 
2976
    `odict <http://www.voidspace.org.uk/python/odict.html>`_ is available ?
 
2977
    
 
2978
    Implement a better ``__repr__`` ? (``ConfigObj({})``)
 
2979
    
 
2980
    Implement some of the sequence methods (which include slicing) from the
 
2981
    newer ``odict`` ?
 
2982
    
 
2983
    INCOMPATIBLE CHANGES
 
2984
    ====================
 
2985
    
 
2986
    (I have removed a lot of needless complications - this list is probably not
 
2987
    conclusive, many option/attribute/method names have changed)
 
2988
    
 
2989
    Case sensitive
 
2990
    
 
2991
    The only valid divider is '='
 
2992
    
 
2993
    We've removed line continuations with '\'
 
2994
    
 
2995
    No recursive lists in values
 
2996
    
 
2997
    No empty section
 
2998
    
 
2999
    No distinction between flatfiles and non flatfiles
 
3000
    
 
3001
    Change in list syntax - use commas to indicate list, not parentheses
 
3002
    (square brackets and parentheses are no longer recognised as lists)
 
3003
    
 
3004
    ';' is no longer valid for comments and no multiline comments
 
3005
    
 
3006
    No attribute access
 
3007
    
 
3008
    We don't allow empty values - have to use '' or ""
 
3009
    
 
3010
    In ConfigObj 3 - setting a non-flatfile member to ``None`` would
 
3011
    initialise it as an empty section.
 
3012
    
 
3013
    The escape entities '&mjf-lf;' and '&mjf-quot;' have gone
 
3014
    replaced by triple quote, multiple line values.
 
3015
    
 
3016
    The ``newline``, ``force_return``, and ``default`` options have gone
 
3017
    
 
3018
    The ``encoding`` and ``backup_encoding`` methods have gone - replaced
 
3019
    with the ``encode`` and ``decode`` methods.
 
3020
    
 
3021
    ``fileerror`` and ``createempty`` options have become ``file_error`` and
 
3022
    ``create_empty``
 
3023
    
 
3024
    Partial configspecs (for specifying the order members should be written
 
3025
    out and which should be present) have gone. The configspec is no longer
 
3026
    used to specify order for the ``write`` method.
 
3027
    
 
3028
    Exceeding the maximum depth of recursion in string interpolation now
 
3029
    raises an error ``InterpolationDepthError``.
 
3030
    
 
3031
    Specifying a value for interpolation which doesn't exist now raises an
 
3032
    error ``MissingInterpolationOption`` (instead of merely being ignored).
 
3033
    
 
3034
    The ``writein`` method has been removed.
 
3035
    
 
3036
    The comments attribute is now a list (``inline_comments`` equates to the
 
3037
    old comments attribute)
 
3038
    
 
3039
    ISSUES
 
3040
    ======
 
3041
    
 
3042
    ``validate`` doesn't report *extra* values or sections.
 
3043
    
 
3044
    You can't have a keyword with the same name as a section (in the same
 
3045
    section). They are both dictionary keys - so they would overlap.
 
3046
    
 
3047
    ConfigObj doesn't quote and unquote values if ``list_values=False``.
 
3048
    This means that leading or trailing whitespace in values will be lost when
 
3049
    writing. (Unless you manually quote).
 
3050
    
 
3051
    Interpolation checks first the 'DEFAULT' subsection of the current
 
3052
    section, next it checks the 'DEFAULT' section of the parent section,
 
3053
    last it checks the 'DEFAULT' section of the main section.
 
3054
    
 
3055
    Logically a 'DEFAULT' section should apply to all subsections of the *same
 
3056
    parent* - this means that checking the 'DEFAULT' subsection in the
 
3057
    *current section* is not necessarily logical ?
 
3058
    
 
3059
    In order to simplify unicode support (which is possibly of limited value
 
3060
    in a config file) I have removed automatic support and added the
 
3061
    ``encode`` and ``decode methods, which can be used to transform keys and
 
3062
    entries. Because the regex looks for specific values on inital parsing
 
3063
    (i.e. the quotes and the equals signs) it can only read ascii compatible
 
3064
    encodings. For unicode use ``UTF8``, which is ASCII compatible.
 
3065
    
 
3066
    Does it matter that we don't support the ':' divider, which is supported
 
3067
    by ``ConfigParser`` ?
 
3068
    
 
3069
    The regular expression correctly removes the value -
 
3070
    ``"'hello', 'goodbye'"`` and then unquote just removes the front and
 
3071
    back quotes (called from ``_handle_value``). What should we do ??
 
3072
    (*ought* to raise exception because it's an invalid value if lists are
 
3073
    off *sigh*. This is not what you want if you want to do your own list
 
3074
    processing - would be *better* in this case not to unquote.)
 
3075
    
 
3076
    String interpolation and validation don't play well together. When
 
3077
    validation changes type it sets the value. This will correctly fetch the
 
3078
    value using interpolation - but then overwrite the interpolation reference.
 
3079
    If the value is unchanged by validation (it's a string) - but other types
 
3080
    will be.
 
3081
    
 
3082
    
 
3083
    List Value Syntax
 
3084
    =================
 
3085
    
 
3086
    List values allow you to specify multiple values for a keyword. This
 
3087
    maps to a list as the resulting Python object when parsed.
 
3088
    
 
3089
    The syntax for lists is easy. A list is a comma separated set of values.
 
3090
    If these values contain quotes, the hash mark, or commas, then the values
 
3091
    can be surrounded by quotes. e.g. : ::
 
3092
    
 
3093
        keyword = value1, 'value 2', "value 3"
 
3094
    
 
3095
    If a value needs to be a list, but only has one member, then you indicate
 
3096
    this with a trailing comma. e.g. : ::
 
3097
    
 
3098
        keyword = "single value",
 
3099
    
 
3100
    If a value needs to be a list, but it has no members, then you indicate
 
3101
    this with a single comma. e.g. : ::
 
3102
    
 
3103
        keyword = ,     # an empty list
 
3104
    
 
3105
    Using triple quotes it will be possible for single values to contain
 
3106
    newlines and *both* single quotes and double quotes. Triple quotes aren't
 
3107
    allowed in list values. This means that the members of list values can't
 
3108
    contain carriage returns (or line feeds :-) or both quote values.
 
3109
      
 
3110
    CHANGELOG
 
3111
    =========
 
3112
    
 
3113
    2006/02/04
 
3114
    ----------
 
3115
    
 
3116
    Removed ``BOM_UTF8`` from ``__all__``.
 
3117
    
 
3118
    The ``BOM`` attribute has become a boolean. (Defaults to ``False``.) It is
 
3119
    *only* ``True`` for the ``UTF16`` encoding.
 
3120
    
 
3121
    File like objects no longer need a ``seek`` attribute.
 
3122
    
 
3123
    ConfigObj no longer keeps a reference to file like objects. Instead the
 
3124
    ``write`` method takes a file like object as an optional argument. (Which
 
3125
    will be used in preference of the ``filename`` attribute if htat exists as
 
3126
    well.)
 
3127
    
 
3128
    Full unicode support added. New options/attributes ``encoding``,
 
3129
    ``default_encoding``.
 
3130
    
 
3131
    utf16 files decoded to unicode.
 
3132
    
 
3133
    If ``BOM`` is ``True``, but no encoding specified, then the utf8 BOM is
 
3134
    written out at the start of the file. (It will normally only be ``True`` if
 
3135
    the utf8 BOM was found when the file was read.)
 
3136
    
 
3137
    File paths are *not* converted to absolute paths, relative paths will
 
3138
    remain relative as the ``filename`` attribute.
 
3139
    
 
3140
    Fixed bug where ``final_comment`` wasn't returned if ``write`` is returning
 
3141
    a list of lines.
 
3142
    
 
3143
    2006/01/31
 
3144
    ----------
 
3145
    
 
3146
    Added ``True``, ``False``, and ``enumerate`` if they are not defined.
 
3147
    (``True`` and ``False`` are needed for *early* versions of Python 2.2,
 
3148
    ``enumerate`` is needed for all versions ofPython 2.2)
 
3149
    
 
3150
    Deprecated ``istrue``, replaced it with ``as_bool``.
 
3151
    
 
3152
    Added ``as_int`` and ``as_float``.
 
3153
    
 
3154
    utf8 and utf16 BOM handled in an endian agnostic way.
 
3155
    
 
3156
    2005/12/14
 
3157
    ----------
 
3158
    
 
3159
    Validation no longer done on the 'DEFAULT' section (only in the root
 
3160
    level). This allows interpolation in configspecs.
 
3161
    
 
3162
    Change in validation syntax implemented in validate 0.2.1
 
3163
    
 
3164
    4.1.0
 
3165
    
 
3166
    2005/12/10
 
3167
    ----------
 
3168
    
 
3169
    Added ``merge``, a recursive update.
 
3170
    
 
3171
    Added ``preserve_errors`` to ``validate`` and the ``flatten_errors``
 
3172
    example function.
 
3173
    
 
3174
    Thanks to Matthew Brett for suggestions and helping me iron out bugs.
 
3175
    
 
3176
    Fixed bug where a config file is *all* comment, the comment will now be
 
3177
    ``initial_comment`` rather than ``final_comment``.
 
3178
    
 
3179
    2005/12/02
 
3180
    ----------
 
3181
    
 
3182
    Fixed bug in ``create_empty``. Thanks to Paul Jimenez for the report.
 
3183
    
 
3184
    2005/11/04
 
3185
    ----------
 
3186
    
 
3187
    Fixed bug in ``Section.walk`` when transforming names as well as values.
 
3188
    
 
3189
    Added the ``istrue`` method. (Fetches the boolean equivalent of a string
 
3190
    value).
 
3191
    
 
3192
    Fixed ``list_values=False`` - they are now only quoted/unquoted if they
 
3193
    are multiline values.
 
3194
    
 
3195
    List values are written as ``item, item`` rather than ``item,item``.
 
3196
    
 
3197
    4.0.1
 
3198
    
 
3199
    2005/10/09
 
3200
    ----------
 
3201
    
 
3202
    Fixed typo in ``write`` method. (Testing for the wrong value when resetting
 
3203
    ``interpolation``).
 
3204
 
 
3205
    4.0.0 Final
 
3206
    
 
3207
    2005/09/16
 
3208
    ----------
 
3209
    
 
3210
    Fixed bug in ``setdefault`` - creating a new section *wouldn't* return
 
3211
    a reference to the new section.
 
3212
    
 
3213
    2005/09/09
 
3214
    ----------
 
3215
    
 
3216
    Removed ``PositionError``.
 
3217
    
 
3218
    Allowed quotes around keys as documented.
 
3219
    
 
3220
    Fixed bug with commas in comments. (matched as a list value)
 
3221
    
 
3222
    Beta 5
 
3223
    
 
3224
    2005/09/07
 
3225
    ----------
 
3226
    
 
3227
    Fixed bug in initialising ConfigObj from a ConfigObj.
 
3228
    
 
3229
    Changed the mailing list address.
 
3230
    
 
3231
    Beta 4
 
3232
    
 
3233
    2005/09/03
 
3234
    ----------
 
3235
    
 
3236
    Fixed bug in ``Section.__delitem__`` oops.
 
3237
    
 
3238
    2005/08/28
 
3239
    ----------
 
3240
    
 
3241
    Interpolation is switched off before writing out files.
 
3242
    
 
3243
    Fixed bug in handling ``StringIO`` instances. (Thanks to report from
 
3244
    "Gustavo Niemeyer" <gustavo@niemeyer.net>)
 
3245
    
 
3246
    Moved the doctests from the ``__init__`` method to a separate function.
 
3247
    (For the sake of IDE calltips).
 
3248
    
 
3249
    Beta 3
 
3250
    
 
3251
    2005/08/26
 
3252
    ----------
 
3253
    
 
3254
    String values unchanged by validation *aren't* reset. This preserves
 
3255
    interpolation in string values.
 
3256
    
 
3257
    2005/08/18
 
3258
    ----------
 
3259
    
 
3260
    None from a default is turned to '' if stringify is off - because setting 
 
3261
    a value to None raises an error.
 
3262
    
 
3263
    Version 4.0.0-beta2
 
3264
    
 
3265
    2005/08/16
 
3266
    ----------
 
3267
    
 
3268
    By Nicola Larosa
 
3269
    
 
3270
    Actually added the RepeatSectionError class ;-)
 
3271
    
 
3272
    2005/08/15
 
3273
    ----------
 
3274
    
 
3275
    If ``stringify`` is off - list values are preserved by the ``validate``
 
3276
    method. (Bugfix)
 
3277
    
 
3278
    2005/08/14
 
3279
    ----------
 
3280
    
 
3281
    By Michael Foord
 
3282
    
 
3283
    Fixed ``simpleVal``.
 
3284
    
 
3285
    Added ``RepeatSectionError`` error if you have additional sections in a
 
3286
    section with a ``__many__`` (repeated) section.
 
3287
    
 
3288
    By Nicola Larosa
 
3289
    
 
3290
    Reworked the ConfigObj._parse, _handle_error and _multiline methods:
 
3291
    mutated the self._infile, self._index and self._maxline attributes into
 
3292
    local variables and method parameters
 
3293
    
 
3294
    Reshaped the ConfigObj._multiline method to better reflect its semantics
 
3295
    
 
3296
    Changed the "default_test" test in ConfigObj.validate to check the fix for
 
3297
    the bug in validate.Validator.check
 
3298
    
 
3299
    2005/08/13
 
3300
    ----------
 
3301
    
 
3302
    By Nicola Larosa
 
3303
    
 
3304
    Updated comments at top
 
3305
    
 
3306
    2005/08/11
 
3307
    ----------
 
3308
    
 
3309
    By Michael Foord
 
3310
    
 
3311
    Implemented repeated sections.
 
3312
    
 
3313
    By Nicola Larosa
 
3314
    
 
3315
    Added test for interpreter version: raises RuntimeError if earlier than
 
3316
    2.2
 
3317
    
 
3318
    2005/08/10
 
3319
    ----------
 
3320
   
 
3321
    By Michael Foord
 
3322
     
 
3323
    Implemented default values in configspecs.
 
3324
    
 
3325
    By Nicola Larosa
 
3326
    
 
3327
    Fixed naked except: clause in validate that was silencing the fact
 
3328
    that Python2.2 does not have dict.pop
 
3329
    
 
3330
    2005/08/08
 
3331
    ----------
 
3332
    
 
3333
    By Michael Foord
 
3334
    
 
3335
    Bug fix causing error if file didn't exist.
 
3336
    
 
3337
    2005/08/07
 
3338
    ----------
 
3339
    
 
3340
    By Nicola Larosa
 
3341
    
 
3342
    Adjusted doctests for Python 2.2.3 compatibility
 
3343
    
 
3344
    2005/08/04
 
3345
    ----------
 
3346
    
 
3347
    By Michael Foord
 
3348
    
 
3349
    Added the inline_comments attribute
 
3350
    
 
3351
    We now preserve and rewrite all comments in the config file
 
3352
    
 
3353
    configspec is now a section attribute
 
3354
    
 
3355
    The validate method changes values in place
 
3356
    
 
3357
    Added InterpolationError
 
3358
    
 
3359
    The errors now have line number, line, and message attributes. This
 
3360
    simplifies error handling
 
3361
    
 
3362
    Added __docformat__
 
3363
    
 
3364
    2005/08/03
 
3365
    ----------
 
3366
    
 
3367
    By Michael Foord
 
3368
    
 
3369
    Fixed bug in Section.pop (now doesn't raise KeyError if a default value
 
3370
    is specified)
 
3371
    
 
3372
    Replaced ``basestring`` with ``types.StringTypes``
 
3373
    
 
3374
    Removed the ``writein`` method
 
3375
    
 
3376
    Added __version__
 
3377
    
 
3378
    2005/07/29
 
3379
    ----------
 
3380
    
 
3381
    By Nicola Larosa
 
3382
    
 
3383
    Indentation in config file is not significant anymore, subsections are
 
3384
    designated by repeating square brackets
 
3385
    
 
3386
    Adapted all tests and docs to the new format
 
3387
    
 
3388
    2005/07/28
 
3389
    ----------
 
3390
    
 
3391
    By Nicola Larosa
 
3392
    
 
3393
    Added more tests
 
3394
    
 
3395
    2005/07/23
 
3396
    ----------
 
3397
    
 
3398
    By Nicola Larosa
 
3399
    
 
3400
    Reformatted final docstring in ReST format, indented it for easier folding
 
3401
    
 
3402
    Code tests converted to doctest format, and scattered them around
 
3403
    in various docstrings
 
3404
    
 
3405
    Walk method rewritten using scalars and sections attributes
 
3406
    
 
3407
    2005/07/22
 
3408
    ----------
 
3409
    
 
3410
    By Nicola Larosa
 
3411
    
 
3412
    Changed Validator and SimpleVal "test" methods to "check"
 
3413
    
 
3414
    More code cleanup
 
3415
    
 
3416
    2005/07/21
 
3417
    ----------
 
3418
    
 
3419
    Changed Section.sequence to Section.scalars and Section.sections
 
3420
    
 
3421
    Added Section.configspec
 
3422
    
 
3423
    Sections in the root section now have no extra indentation
 
3424
    
 
3425
    Comments now better supported in Section and preserved by ConfigObj
 
3426
    
 
3427
    Comments also written out
 
3428
    
 
3429
    Implemented initial_comment and final_comment
 
3430
    
 
3431
    A scalar value after a section will now raise an error
 
3432
    
 
3433
    2005/07/20
 
3434
    ----------
 
3435
    
 
3436
    Fixed a couple of bugs
 
3437
    
 
3438
    Can now pass a tuple instead of a list
 
3439
    
 
3440
    Simplified dict and walk methods
 
3441
    
 
3442
    Added __str__ to Section
 
3443
    
 
3444
    2005/07/10
 
3445
    ----------
 
3446
    
 
3447
    By Nicola Larosa
 
3448
    
 
3449
    More code cleanup
 
3450
    
 
3451
    2005/07/08
 
3452
    ----------
 
3453
    
 
3454
    The stringify option implemented. On by default.
 
3455
    
 
3456
    2005/07/07
 
3457
    ----------
 
3458
    
 
3459
    Renamed private attributes with a single underscore prefix.
 
3460
    
 
3461
    Changes to interpolation - exceeding recursion depth, or specifying a
 
3462
    missing value, now raise errors.
 
3463
    
 
3464
    Changes for Python 2.2 compatibility. (changed boolean tests - removed
 
3465
    ``is True`` and ``is False``)
 
3466
    
 
3467
    Added test for duplicate section and member (and fixed bug)
 
3468
    
 
3469
    2005/07/06
 
3470
    ----------
 
3471
    
 
3472
    By Nicola Larosa
 
3473
    
 
3474
    Code cleanup
 
3475
    
 
3476
    2005/07/02
 
3477
    ----------
 
3478
    
 
3479
    Version 0.1.0
 
3480
    
 
3481
    Now properly handles values including comments and lists.
 
3482
    
 
3483
    Better error handling.
 
3484
    
 
3485
    String interpolation.
 
3486
    
 
3487
    Some options implemented.
 
3488
    
 
3489
    You can pass a Section a dictionary to initialise it.
 
3490
    
 
3491
    Setting a Section member to a dictionary will create a Section instance.
 
3492
    
 
3493
    2005/06/26
 
3494
    ----------
 
3495
    
 
3496
    Version 0.0.1
 
3497
    
 
3498
    Experimental reader.
 
3499
    
 
3500
    A reasonably elegant implementation - a basic reader in 160 lines of code.
 
3501
    
 
3502
    *A programming language is a medium of expression.* - Paul Graham
 
3503
"""
 
3504