~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Ian Clatworthy
  • Date: 2009-01-19 02:24:15 UTC
  • mto: This revision was merged to the branch mainline in revision 3944.
  • Revision ID: ian.clatworthy@canonical.com-20090119022415-mo0mcfeiexfktgwt
apply jam's log --short fix (Ian Clatworthy)

Show diffs side-by-side

added added

removed removed

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