~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Vincent Ladeuil
  • Date: 2007-11-15 11:28:37 UTC
  • mto: (3017.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 3018.
  • Revision ID: v.ladeuil+lp@free.fr-20071115112837-8wyszawjlg8d34k2
Update configobj to version 4.4.0:
* bzrlib/util/configobj/configobj.py
* bzrlib/util/configobj/docs/BSD-LICENSE.txt 
* bzrlib/util/configobj/docs/configobj.txt 
* bzrlib/util/configobj/docs/validate.txt 

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