~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: John Arbash Meinel
  • Date: 2008-08-25 21:50:11 UTC
  • mfrom: (0.11.3 tools)
  • mto: This revision was merged to the branch mainline in revision 3659.
  • Revision ID: john@arbash-meinel.com-20080825215011-de9esmzgkue3e522
Merge in Lukáš's helper scripts.
Update the packaging documents to describe how to do the releases
using bzr-builddeb to package all distro platforms
simultaneously.

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