~bzr-pqm/bzr/bzr.dev

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