~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Patch Queue Manager
  • Date: 2016-04-21 04:10:52 UTC
  • mfrom: (6616.1.1 fix-en-user-guide)
  • Revision ID: pqm@pqm.ubuntu.com-20160421041052-clcye7ns1qcl2n7w
(richard-wilbur) Ensure build of English use guide always uses English text
 even when user's locale specifies a different language. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
# configobj.py
2
2
# A config file reader/writer that supports nested sections in config files.
3
 
# Copyright (C) 2005-2006 Michael Foord, Nicola Larosa
 
3
# Copyright (C) 2005-2009 Michael Foord, Nicola Larosa
4
4
# E-mail: fuzzyman AT voidspace DOT org DOT uk
5
5
#         nico AT tekNico DOT net
6
6
 
16
16
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
17
17
# Comments, suggestions and bug reports welcome.
18
18
 
19
 
from __future__ import generators
 
19
 
 
20
from __future__ import absolute_import
20
21
 
21
22
import sys
22
 
INTP_VER = sys.version_info[:2]
23
 
if INTP_VER < (2, 2):
24
 
    raise RuntimeError("Python v.2.2 or later needed")
 
23
import os
 
24
import re
25
25
 
26
 
import os, re
27
26
compiler = None
28
 
try:
29
 
    import compiler
30
 
except ImportError:
31
 
    # for IronPython
32
 
    pass
33
 
from types import StringTypes
34
 
from warnings import warn
 
27
# Bzr modification: Disabled import of 'compiler' module
 
28
# bzr doesn't use the 'unrepr' feature of configobj, so importing compiler just
 
29
# wastes several milliseconds on every single bzr invocation.
 
30
#   -- Andrew Bennetts, 2008-10-14
 
31
#try:
 
32
#    import compiler
 
33
#except ImportError:
 
34
#    # for IronPython
 
35
#    pass
 
36
 
 
37
 
35
38
try:
36
39
    from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
37
40
except ImportError:
88
91
    None: BOM_UTF8
89
92
    }
90
93
 
91
 
try:
92
 
    from validate import VdtMissingValue
93
 
except ImportError:
94
 
    VdtMissingValue = None
 
94
 
 
95
def match_utf8(encoding):
 
96
    return BOM_LIST.get(encoding.lower()) == 'utf_8'
 
97
 
 
98
 
 
99
# Quote strings used for writing values
 
100
squot = "'%s'"
 
101
dquot = '"%s"'
 
102
noquot = "%s"
 
103
wspace_plus = ' \r\n\v\t\'"'
 
104
tsquot = '"""%s"""'
 
105
tdquot = "'''%s'''"
95
106
 
96
107
try:
97
108
    enumerate
103
114
            i += 1
104
115
            yield i, item
105
116
 
106
 
try:
107
 
    True, False
108
 
except NameError:
109
 
    True, False = 1, 0
110
 
 
111
 
 
112
 
__version__ = '4.4.0'
 
117
# Sentinel for use in getattr calls to replace hasattr
 
118
MISSING = object()
 
119
 
 
120
__version__ = '4.6.0'
113
121
 
114
122
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
115
123
 
130
138
    'InterpolationLoopError',
131
139
    'MissingInterpolationOption',
132
140
    'RepeatSectionError',
 
141
    'ReloadError',
133
142
    'UnreprError',
134
143
    'UnknownType',
135
144
    '__docformat__',
157
166
}
158
167
 
159
168
 
 
169
 
160
170
def getObj(s):
161
171
    s = "a=" + s
162
172
    if compiler is None:
164
174
    p = compiler.parse(s)
165
175
    return p.getChildren()[1].getChildren()[0].getChildren()[1]
166
176
 
 
177
 
167
178
class UnknownType(Exception):
168
179
    pass
169
180
 
170
 
class Builder:
171
 
    
 
181
 
 
182
class Builder(object):
 
183
 
172
184
    def build(self, o):
173
185
        m = getattr(self, 'build_' + o.__class__.__name__, None)
174
186
        if m is None:
175
187
            raise UnknownType(o.__class__.__name__)
176
188
        return m(o)
177
 
    
 
189
 
178
190
    def build_List(self, o):
179
191
        return map(self.build, o.getChildren())
180
 
    
 
192
 
181
193
    def build_Const(self, o):
182
194
        return o.value
183
 
    
 
195
 
184
196
    def build_Dict(self, o):
185
197
        d = {}
186
198
        i = iter(map(self.build, o.getChildren()))
187
199
        for el in i:
188
200
            d[el] = i.next()
189
201
        return d
190
 
    
 
202
 
191
203
    def build_Tuple(self, o):
192
204
        return tuple(self.build_List(o))
193
 
    
 
205
 
194
206
    def build_Name(self, o):
195
207
        if o.name == 'None':
196
208
            return None
198
210
            return True
199
211
        if o.name == 'False':
200
212
            return False
201
 
        
202
 
        # An undefinted Name
 
213
 
 
214
        # An undefined Name
203
215
        raise UnknownType('Undefined Name')
204
 
    
 
216
 
205
217
    def build_Add(self, o):
206
218
        real, imag = map(self.build_Const, o.getChildren())
207
219
        try:
211
223
        if not isinstance(imag, complex) or imag.real != 0.0:
212
224
            raise UnknownType('Add')
213
225
        return real+imag
214
 
    
 
226
 
215
227
    def build_Getattr(self, o):
216
228
        parent = self.build(o.expr)
217
229
        return getattr(parent, o.attrname)
218
 
    
 
230
 
219
231
    def build_UnarySub(self, o):
220
232
        return -self.build_Const(o.getChildren()[0])
221
 
    
 
233
 
222
234
    def build_UnaryAdd(self, o):
223
235
        return self.build_Const(o.getChildren()[0])
224
236
 
 
237
 
 
238
_builder = Builder()
 
239
 
 
240
 
225
241
def unrepr(s):
226
242
    if not s:
227
243
        return s
228
 
    return Builder().build(getObj(s))
229
 
 
230
 
def _splitlines(instring):
231
 
    """Split a string on lines, without losing line endings or truncating."""
232
 
    
 
244
    return _builder.build(getObj(s))
 
245
 
 
246
 
233
247
 
234
248
class ConfigObjError(SyntaxError):
235
249
    """
239
253
    def __init__(self, message='', line_number=None, line=''):
240
254
        self.line = line
241
255
        self.line_number = line_number
242
 
        self.message = message
243
256
        SyntaxError.__init__(self, message)
244
257
 
 
258
 
245
259
class NestingError(ConfigObjError):
246
260
    """
247
261
    This error indicates a level of nesting that doesn't match.
248
262
    """
249
263
 
 
264
 
250
265
class ParseError(ConfigObjError):
251
266
    """
252
267
    This error indicates that a line is badly written.
254
269
    nor a valid section marker line.
255
270
    """
256
271
 
 
272
 
 
273
class ReloadError(IOError):
 
274
    """
 
275
    A 'reload' operation failed.
 
276
    This exception is a subclass of ``IOError``.
 
277
    """
 
278
    def __init__(self):
 
279
        IOError.__init__(self, 'reload failed, filename is not set.')
 
280
 
 
281
 
257
282
class DuplicateError(ConfigObjError):
258
283
    """
259
284
    The keyword or section specified already exists.
260
285
    """
261
286
 
 
287
 
262
288
class ConfigspecError(ConfigObjError):
263
289
    """
264
290
    An error occured whilst parsing a configspec.
265
291
    """
266
292
 
 
293
 
267
294
class InterpolationError(ConfigObjError):
268
295
    """Base class for the two interpolation errors."""
269
296
 
 
297
 
270
298
class InterpolationLoopError(InterpolationError):
271
299
    """Maximum interpolation depth exceeded in string interpolation."""
272
300
 
275
303
            self,
276
304
            'interpolation loop detected in value "%s".' % option)
277
305
 
 
306
 
278
307
class RepeatSectionError(ConfigObjError):
279
308
    """
280
309
    This error indicates additional sections in a section with a
281
310
    ``__many__`` (repeated) section.
282
311
    """
283
312
 
 
313
 
284
314
class MissingInterpolationOption(InterpolationError):
285
315
    """A value specified for interpolation was missing."""
286
316
 
289
319
            self,
290
320
            'missing option "%s" in interpolation.' % option)
291
321
 
 
322
 
292
323
class UnreprError(ConfigObjError):
293
324
    """An error parsing in unrepr mode."""
294
325
 
295
326
 
 
327
 
296
328
class InterpolationEngine(object):
297
329
    """
298
330
    A helper class to help perform string interpolation.
308
340
        # the Section instance that "owns" this engine
309
341
        self.section = section
310
342
 
 
343
 
311
344
    def interpolate(self, key, value):
312
345
        def recursive_interpolate(key, value, section, backtrail):
313
346
            """The function that does the actual work.
320
353
            This is similar to a depth-first-search algorithm.
321
354
            """
322
355
            # Have we been here already?
323
 
            if backtrail.has_key((key, section.name)):
 
356
            if (key, section.name) in backtrail:
324
357
                # Yes - infinite loop detected
325
358
                raise InterpolationLoopError(key)
326
359
            # Place a marker on our backtrail so we won't come back here again
356
389
        value = recursive_interpolate(key, value, self.section, {})
357
390
        return value
358
391
 
 
392
 
359
393
    def _fetch(self, key):
360
394
        """Helper function to fetch values from owning section.
361
395
 
389
423
            raise MissingInterpolationOption(key)
390
424
        return val, current_section
391
425
 
 
426
 
392
427
    def _parse_match(self, match):
393
428
        """Implementation-dependent helper function.
394
429
 
405
440
        interpolation should be performed on the resulting value
406
441
        (e.g., if we interpolated "$$" and returned "$").
407
442
        """
408
 
        raise NotImplementedError
409
 
    
 
443
        raise NotImplementedError()
 
444
 
 
445
 
410
446
 
411
447
class ConfigParserInterpolation(InterpolationEngine):
412
448
    """Behaves like ConfigParser."""
418
454
        return key, value, section
419
455
 
420
456
 
 
457
 
421
458
class TemplateInterpolation(InterpolationEngine):
422
459
    """Behaves like string.Template."""
423
460
    _delimiter = '$'
442
479
        # Anything else: ignore completely, just return it unchanged
443
480
        return None, match.group(), None
444
481
 
 
482
 
445
483
interpolation_engines = {
446
484
    'configparser': ConfigParserInterpolation,
447
485
    'template': TemplateInterpolation,
448
486
}
449
487
 
 
488
 
 
489
def __newobj__(cls, *args):
 
490
    # Hack for pickle
 
491
    return cls.__new__(cls, *args)
 
492
 
450
493
class Section(dict):
451
494
    """
452
495
    A dictionary-like object that represents a section in a config file.
453
 
    
 
496
 
454
497
    It does string interpolation if the 'interpolation' attribute
455
498
    of the 'main' object is set to True.
456
 
    
 
499
 
457
500
    Interpolation is tried first from this object, then from the 'DEFAULT'
458
501
    section of this object, next from the parent and its 'DEFAULT' section,
459
502
    and so on until the main object is reached.
460
 
    
 
503
 
461
504
    A Section will behave like an ordered dictionary - following the
462
505
    order of the ``scalars`` and ``sections`` attributes.
463
506
    You can use this to change the order of members.
464
 
    
 
507
 
465
508
    Iteration follows the order: scalars, then sections.
466
509
    """
467
510
 
 
511
 
 
512
    def __setstate__(self, state):
 
513
        dict.update(self, state[0])
 
514
        self.__dict__.update(state[1])
 
515
 
 
516
    def __reduce__(self):
 
517
        state = (dict(self), self.__dict__)
 
518
        return (__newobj__, (self.__class__,), state)
 
519
 
 
520
 
468
521
    def __init__(self, parent, depth, main, indict=None, name=None):
469
522
        """
470
523
        * parent is the section above
481
534
        self.main = main
482
535
        # level of nesting depth of this Section
483
536
        self.depth = depth
 
537
        # purely for information
 
538
        self.name = name
 
539
        #
 
540
        self._initialise()
 
541
        # we do this explicitly so that __setitem__ is used properly
 
542
        # (rather than just passing to ``dict.__init__``)
 
543
        for entry, value in indict.iteritems():
 
544
            self[entry] = value
 
545
 
 
546
 
 
547
    def _initialise(self):
484
548
        # the sequence of scalar values in this Section
485
549
        self.scalars = []
486
550
        # the sequence of sections in this Section
487
551
        self.sections = []
488
 
        # purely for information
489
 
        self.name = name
490
552
        # for comments :-)
491
553
        self.comments = {}
492
554
        self.inline_comments = {}
493
 
        # for the configspec
494
 
        self.configspec = {}
495
 
        self._order = []
496
 
        self._configspec_comments = {}
497
 
        self._configspec_inline_comments = {}
498
 
        self._cs_section_comments = {}
499
 
        self._cs_section_inline_comments = {}
 
555
        # the configspec
 
556
        self.configspec = None
500
557
        # for defaults
501
558
        self.defaults = []
502
 
        #
503
 
        # we do this explicitly so that __setitem__ is used properly
504
 
        # (rather than just passing to ``dict.__init__``)
505
 
        for entry in indict:
506
 
            self[entry] = indict[entry]
 
559
        self.default_values = {}
 
560
 
507
561
 
508
562
    def _interpolate(self, key, value):
509
563
        try:
527
581
        # let the engine do the actual work
528
582
        return engine.interpolate(key, value)
529
583
 
 
584
 
530
585
    def __getitem__(self, key):
531
586
        """Fetch the item and do string interpolation."""
532
587
        val = dict.__getitem__(self, key)
533
 
        if self.main.interpolation and isinstance(val, StringTypes):
 
588
        if self.main.interpolation and isinstance(val, basestring):
534
589
            return self._interpolate(key, val)
535
590
        return val
536
591
 
 
592
 
537
593
    def __setitem__(self, key, value, unrepr=False):
538
594
        """
539
595
        Correctly set a value.
540
 
        
 
596
 
541
597
        Making dictionary values Section instances.
542
598
        (We have to special case 'Section' instances - which are also dicts)
543
 
        
 
599
 
544
600
        Keys must be strings.
545
601
        Values need only be strings (or lists of strings) if
546
602
        ``main.stringify`` is set.
547
 
        
548
 
        `unrepr`` must be set when setting a value to a dictionary, without
 
603
 
 
604
        ``unrepr`` must be set when setting a value to a dictionary, without
549
605
        creating a new sub-section.
550
606
        """
551
 
        if not isinstance(key, StringTypes):
552
 
            raise ValueError, 'The key "%s" is not a string.' % key
 
607
        if not isinstance(key, basestring):
 
608
            raise ValueError('The key "%s" is not a string.' % key)
 
609
 
553
610
        # add the comment
554
 
        if not self.comments.has_key(key):
 
611
        if key not in self.comments:
555
612
            self.comments[key] = []
556
613
            self.inline_comments[key] = ''
557
614
        # remove the entry from defaults
559
616
            self.defaults.remove(key)
560
617
        #
561
618
        if isinstance(value, Section):
562
 
            if not self.has_key(key):
 
619
            if key not in self:
563
620
                self.sections.append(key)
564
621
            dict.__setitem__(self, key, value)
565
622
        elif isinstance(value, dict) and not unrepr:
566
623
            # First create the new depth level,
567
624
            # then create the section
568
 
            if not self.has_key(key):
 
625
            if key not in self:
569
626
                self.sections.append(key)
570
627
            new_depth = self.depth + 1
571
628
            dict.__setitem__(
578
635
                    indict=value,
579
636
                    name=key))
580
637
        else:
581
 
            if not self.has_key(key):
 
638
            if key not in self:
582
639
                self.scalars.append(key)
583
640
            if not self.main.stringify:
584
 
                if isinstance(value, StringTypes):
 
641
                if isinstance(value, basestring):
585
642
                    pass
586
643
                elif isinstance(value, (list, tuple)):
587
644
                    for entry in value:
588
 
                        if not isinstance(entry, StringTypes):
589
 
                            raise TypeError, (
590
 
                                'Value is not a string "%s".' % entry)
 
645
                        if not isinstance(entry, basestring):
 
646
                            raise TypeError('Value is not a string "%s".' % entry)
591
647
                else:
592
 
                    raise TypeError, 'Value is not a string "%s".' % value
 
648
                    raise TypeError('Value is not a string "%s".' % value)
593
649
            dict.__setitem__(self, key, value)
594
650
 
 
651
 
595
652
    def __delitem__(self, key):
596
653
        """Remove items from the sequence when deleting."""
597
654
        dict. __delitem__(self, key)
602
659
        del self.comments[key]
603
660
        del self.inline_comments[key]
604
661
 
 
662
 
605
663
    def get(self, key, default=None):
606
664
        """A version of ``get`` that doesn't bypass string interpolation."""
607
665
        try:
609
667
        except KeyError:
610
668
            return default
611
669
 
 
670
 
612
671
    def update(self, indict):
613
672
        """
614
673
        A version of update that uses our ``__setitem__``.
616
675
        for entry in indict:
617
676
            self[entry] = indict[entry]
618
677
 
 
678
 
619
679
    def pop(self, key, *args):
620
 
        """ """
 
680
        """
 
681
        'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 
682
        If key is not found, d is returned if given, otherwise KeyError is raised'
 
683
        """
621
684
        val = dict.pop(self, key, *args)
622
685
        if key in self.scalars:
623
686
            del self.comments[key]
627
690
            del self.comments[key]
628
691
            del self.inline_comments[key]
629
692
            self.sections.remove(key)
630
 
        if self.main.interpolation and isinstance(val, StringTypes):
 
693
        if self.main.interpolation and isinstance(val, basestring):
631
694
            return self._interpolate(key, val)
632
695
        return val
633
696
 
 
697
 
634
698
    def popitem(self):
635
699
        """Pops the first (key,val)"""
636
700
        sequence = (self.scalars + self.sections)
637
701
        if not sequence:
638
 
            raise KeyError, ": 'popitem(): dictionary is empty'"
 
702
            raise KeyError(": 'popitem(): dictionary is empty'")
639
703
        key = sequence[0]
640
704
        val =  self[key]
641
705
        del self[key]
642
706
        return key, val
643
707
 
 
708
 
644
709
    def clear(self):
645
710
        """
646
711
        A version of clear that also affects scalars/sections
647
712
        Also clears comments and configspec.
648
 
        
 
713
 
649
714
        Leaves other attributes alone :
650
715
            depth/main/parent are not affected
651
716
        """
654
719
        self.sections = []
655
720
        self.comments = {}
656
721
        self.inline_comments = {}
657
 
        self.configspec = {}
 
722
        self.configspec = None
 
723
 
658
724
 
659
725
    def setdefault(self, key, default=None):
660
726
        """A version of setdefault that sets sequence if appropriate."""
664
730
            self[key] = default
665
731
            return self[key]
666
732
 
 
733
 
667
734
    def items(self):
668
 
        """ """
 
735
        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
669
736
        return zip((self.scalars + self.sections), self.values())
670
737
 
 
738
 
671
739
    def keys(self):
672
 
        """ """
 
740
        """D.keys() -> list of D's keys"""
673
741
        return (self.scalars + self.sections)
674
742
 
 
743
 
675
744
    def values(self):
676
 
        """ """
 
745
        """D.values() -> list of D's values"""
677
746
        return [self[key] for key in (self.scalars + self.sections)]
678
747
 
 
748
 
679
749
    def iteritems(self):
680
 
        """ """
 
750
        """D.iteritems() -> an iterator over the (key, value) items of D"""
681
751
        return iter(self.items())
682
752
 
 
753
 
683
754
    def iterkeys(self):
684
 
        """ """
 
755
        """D.iterkeys() -> an iterator over the keys of D"""
685
756
        return iter((self.scalars + self.sections))
686
757
 
687
758
    __iter__ = iterkeys
688
759
 
 
760
 
689
761
    def itervalues(self):
690
 
        """ """
 
762
        """D.itervalues() -> an iterator over the values of D"""
691
763
        return iter(self.values())
692
764
 
 
765
 
693
766
    def __repr__(self):
 
767
        """x.__repr__() <==> repr(x)"""
694
768
        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
695
769
            for key in (self.scalars + self.sections)])
696
770
 
697
771
    __str__ = __repr__
 
772
    __str__.__doc__ = "x.__str__() <==> str(x)"
 
773
 
698
774
 
699
775
    # Extra methods - not in a normal dictionary
700
776
 
701
777
    def dict(self):
702
778
        """
703
779
        Return a deepcopy of self as a dictionary.
704
 
        
 
780
 
705
781
        All members that are ``Section`` instances are recursively turned to
706
782
        ordinary dictionaries - by calling their ``dict`` method.
707
 
        
 
783
 
708
784
        >>> n = a.dict()
709
785
        >>> n == a
710
786
        1
725
801
            newdict[entry] = this_entry
726
802
        return newdict
727
803
 
 
804
 
728
805
    def merge(self, indict):
729
806
        """
730
807
        A recursive update - useful for merging config files.
731
 
        
 
808
 
732
809
        >>> a = '''[section1]
733
810
        ...     option1 = True
734
811
        ...     [[subsection]]
742
819
        >>> c2 = ConfigObj(a)
743
820
        >>> c2.merge(c1)
744
821
        >>> c2
745
 
        {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
 
822
        ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
746
823
        """
747
824
        for key, val in indict.items():
748
825
            if (key in self and isinstance(self[key], dict) and
749
826
                                isinstance(val, dict)):
750
827
                self[key].merge(val)
751
 
            else:   
 
828
            else:
752
829
                self[key] = val
753
830
 
 
831
 
754
832
    def rename(self, oldkey, newkey):
755
833
        """
756
834
        Change a keyname to another, without changing position in sequence.
757
 
        
 
835
 
758
836
        Implemented so that transformations can be made on keys,
759
837
        as well as on values. (used by encode and decode)
760
 
        
 
838
 
761
839
        Also renames comments.
762
840
        """
763
841
        if oldkey in self.scalars:
765
843
        elif oldkey in self.sections:
766
844
            the_list = self.sections
767
845
        else:
768
 
            raise KeyError, 'Key "%s" not found.' % oldkey
 
846
            raise KeyError('Key "%s" not found.' % oldkey)
769
847
        pos = the_list.index(oldkey)
770
848
        #
771
849
        val = self[oldkey]
780
858
        self.comments[newkey] = comm
781
859
        self.inline_comments[newkey] = inline_comment
782
860
 
 
861
 
783
862
    def walk(self, function, raise_errors=True,
784
863
            call_on_sections=False, **keywargs):
785
864
        """
786
865
        Walk every member and call a function on the keyword and value.
787
 
        
 
866
 
788
867
        Return a dictionary of the return values
789
 
        
 
868
 
790
869
        If the function raises an exception, raise the errror
791
870
        unless ``raise_errors=False``, in which case set the return value to
792
871
        ``False``.
793
 
        
 
872
 
794
873
        Any unrecognised keyword arguments you pass to walk, will be pased on
795
874
        to the function you pass in.
796
 
        
 
875
 
797
876
        Note: if ``call_on_sections`` is ``True`` then - on encountering a
798
877
        subsection, *first* the function is called for the *whole* subsection,
799
878
        and then recurses into it's members. This means your function must be
800
879
        able to handle strings, dictionaries and lists. This allows you
801
880
        to change the key of subsections as well as for ordinary members. The
802
881
        return value when called on the whole subsection has to be discarded.
803
 
        
 
882
 
804
883
        See  the encode and decode methods for examples, including functions.
805
 
        
806
 
        .. caution::
807
 
        
 
884
 
 
885
        .. admonition:: caution
 
886
 
808
887
            You can use ``walk`` to transform the names of members of a section
809
888
            but you mustn't add or delete members.
810
 
        
 
889
 
811
890
        >>> config = '''[XXXXsection]
812
891
        ... XXXXkey = XXXXvalue'''.splitlines()
813
892
        >>> cfg = ConfigObj(config)
814
893
        >>> cfg
815
 
        {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
 
894
        ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
816
895
        >>> def transform(section, key):
817
896
        ...     val = section[key]
818
897
        ...     newkey = key.replace('XXXX', 'CLIENT1')
825
904
        >>> cfg.walk(transform, call_on_sections=True)
826
905
        {'CLIENT1section': {'CLIENT1key': None}}
827
906
        >>> cfg
828
 
        {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
 
907
        ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
829
908
        """
830
909
        out = {}
831
910
        # scalars first
864
943
                **keywargs)
865
944
        return out
866
945
 
867
 
    def decode(self, encoding):
868
 
        """
869
 
        Decode all strings and values to unicode, using the specified encoding.
870
 
        
871
 
        Works with subsections and list values.
872
 
        
873
 
        Uses the ``walk`` method.
874
 
        
875
 
        Testing ``encode`` and ``decode``.
876
 
        >>> m = ConfigObj(a)
877
 
        >>> m.decode('ascii')
878
 
        >>> def testuni(val):
879
 
        ...     for entry in val:
880
 
        ...         if not isinstance(entry, unicode):
881
 
        ...             print >> sys.stderr, type(entry)
882
 
        ...             raise AssertionError, 'decode failed.'
883
 
        ...         if isinstance(val[entry], dict):
884
 
        ...             testuni(val[entry])
885
 
        ...         elif not isinstance(val[entry], unicode):
886
 
        ...             raise AssertionError, 'decode failed.'
887
 
        >>> testuni(m)
888
 
        >>> m.encode('ascii')
889
 
        >>> a == m
890
 
        1
891
 
        """
892
 
        warn('use of ``decode`` is deprecated.', DeprecationWarning)
893
 
        def decode(section, key, encoding=encoding, warn=True):
894
 
            """ """
895
 
            val = section[key]
896
 
            if isinstance(val, (list, tuple)):
897
 
                newval = []
898
 
                for entry in val:
899
 
                    newval.append(entry.decode(encoding))
900
 
            elif isinstance(val, dict):
901
 
                newval = val
902
 
            else:
903
 
                newval = val.decode(encoding)
904
 
            newkey = key.decode(encoding)
905
 
            section.rename(key, newkey)
906
 
            section[newkey] = newval
907
 
        # using ``call_on_sections`` allows us to modify section names
908
 
        self.walk(decode, call_on_sections=True)
909
 
 
910
 
    def encode(self, encoding):
911
 
        """
912
 
        Encode all strings and values from unicode,
913
 
        using the specified encoding.
914
 
        
915
 
        Works with subsections and list values.
916
 
        Uses the ``walk`` method.
917
 
        """
918
 
        warn('use of ``encode`` is deprecated.', DeprecationWarning)
919
 
        def encode(section, key, encoding=encoding):
920
 
            """ """
921
 
            val = section[key]
922
 
            if isinstance(val, (list, tuple)):
923
 
                newval = []
924
 
                for entry in val:
925
 
                    newval.append(entry.encode(encoding))
926
 
            elif isinstance(val, dict):
927
 
                newval = val
928
 
            else:
929
 
                newval = val.encode(encoding)
930
 
            newkey = key.encode(encoding)
931
 
            section.rename(key, newkey)
932
 
            section[newkey] = newval
933
 
        self.walk(encode, call_on_sections=True)
934
 
 
935
 
    def istrue(self, key):
936
 
        """A deprecated version of ``as_bool``."""
937
 
        warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
938
 
                'instead.', DeprecationWarning)
939
 
        return self.as_bool(key)
940
946
 
941
947
    def as_bool(self, key):
942
948
        """
943
949
        Accepts a key as input. The corresponding value must be a string or
944
950
        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
945
951
        retain compatibility with Python 2.2.
946
 
        
947
 
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
 
952
 
 
953
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns
948
954
        ``True``.
949
 
        
950
 
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
 
955
 
 
956
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns
951
957
        ``False``.
952
 
        
 
958
 
953
959
        ``as_bool`` is not case sensitive.
954
 
        
 
960
 
955
961
        Any other input will raise a ``ValueError``.
956
 
        
 
962
 
957
963
        >>> a = ConfigObj()
958
964
        >>> a['a'] = 'fish'
959
965
        >>> a.as_bool('a')
973
979
            return False
974
980
        else:
975
981
            try:
976
 
                if not isinstance(val, StringTypes):
977
 
                    raise KeyError
 
982
                if not isinstance(val, basestring):
 
983
                    # TODO: Why do we raise a KeyError here?
 
984
                    raise KeyError()
978
985
                else:
979
986
                    return self.main._bools[val.lower()]
980
987
            except KeyError:
981
988
                raise ValueError('Value "%s" is neither True nor False' % val)
982
989
 
 
990
 
983
991
    def as_int(self, key):
984
992
        """
985
993
        A convenience method which coerces the specified value to an integer.
986
 
        
 
994
 
987
995
        If the value is an invalid literal for ``int``, a ``ValueError`` will
988
996
        be raised.
989
 
        
 
997
 
990
998
        >>> a = ConfigObj()
991
999
        >>> a['a'] = 'fish'
992
1000
        >>> a.as_int('a')
993
1001
        Traceback (most recent call last):
994
 
        ValueError: invalid literal for int(): fish
 
1002
        ValueError: invalid literal for int() with base 10: 'fish'
995
1003
        >>> a['b'] = '1'
996
1004
        >>> a.as_int('b')
997
1005
        1
998
1006
        >>> a['b'] = '3.2'
999
1007
        >>> a.as_int('b')
1000
1008
        Traceback (most recent call last):
1001
 
        ValueError: invalid literal for int(): 3.2
 
1009
        ValueError: invalid literal for int() with base 10: '3.2'
1002
1010
        """
1003
1011
        return int(self[key])
1004
1012
 
 
1013
 
1005
1014
    def as_float(self, key):
1006
1015
        """
1007
1016
        A convenience method which coerces the specified value to a float.
1008
 
        
 
1017
 
1009
1018
        If the value is an invalid literal for ``float``, a ``ValueError`` will
1010
1019
        be raised.
1011
 
        
 
1020
 
1012
1021
        >>> a = ConfigObj()
1013
1022
        >>> a['a'] = 'fish'
1014
1023
        >>> a.as_float('a')
1022
1031
        3.2000000000000002
1023
1032
        """
1024
1033
        return float(self[key])
1025
 
    
 
1034
 
 
1035
 
 
1036
    def as_list(self, key):
 
1037
        """
 
1038
        A convenience method which fetches the specified value, guaranteeing
 
1039
        that it is a list.
 
1040
 
 
1041
        >>> a = ConfigObj()
 
1042
        >>> a['a'] = 1
 
1043
        >>> a.as_list('a')
 
1044
        [1]
 
1045
        >>> a['a'] = (1,)
 
1046
        >>> a.as_list('a')
 
1047
        [1]
 
1048
        >>> a['a'] = [1]
 
1049
        >>> a.as_list('a')
 
1050
        [1]
 
1051
        """
 
1052
        result = self[key]
 
1053
        if isinstance(result, (tuple, list)):
 
1054
            return list(result)
 
1055
        return [result]
 
1056
 
 
1057
 
 
1058
    def restore_default(self, key):
 
1059
        """
 
1060
        Restore (and return) default value for the specified key.
 
1061
 
 
1062
        This method will only work for a ConfigObj that was created
 
1063
        with a configspec and has been validated.
 
1064
 
 
1065
        If there is no default value for this key, ``KeyError`` is raised.
 
1066
        """
 
1067
        default = self.default_values[key]
 
1068
        dict.__setitem__(self, key, default)
 
1069
        if key not in self.defaults:
 
1070
            self.defaults.append(key)
 
1071
        return default
 
1072
 
 
1073
 
 
1074
    def restore_defaults(self):
 
1075
        """
 
1076
        Recursively restore default values to all members
 
1077
        that have them.
 
1078
 
 
1079
        This method will only work for a ConfigObj that was created
 
1080
        with a configspec and has been validated.
 
1081
 
 
1082
        It doesn't delete or modify entries without default values.
 
1083
        """
 
1084
        for key in self.default_values:
 
1085
            self.restore_default(key)
 
1086
 
 
1087
        for section in self.sections:
 
1088
            self[section].restore_defaults()
 
1089
 
1026
1090
 
1027
1091
class ConfigObj(Section):
1028
1092
    """An object to read, create, and write config files."""
1126
1190
        'true': True, 'false': False,
1127
1191
        }
1128
1192
 
1129
 
    def __init__(self, infile=None, options=None, **kwargs):
 
1193
 
 
1194
    def __init__(self, infile=None, options=None, _inspec=False, **kwargs):
1130
1195
        """
1131
 
        Parse or create a config file object.
1132
 
        
 
1196
        Parse a config file or create a config file object.
 
1197
 
1133
1198
        ``ConfigObj(infile=None, options=None, **kwargs)``
1134
1199
        """
1135
 
        if infile is None:
1136
 
            infile = []
1137
 
        if options is None:
1138
 
            options = {}
1139
 
        else:
1140
 
            options = dict(options)
 
1200
        self._inspec = _inspec
 
1201
        # init the superclass
 
1202
        Section.__init__(self, self, 0, self)
 
1203
 
 
1204
        infile = infile or []
 
1205
        options = dict(options or {})
 
1206
 
1141
1207
        # keyword arguments take precedence over an options dictionary
1142
1208
        options.update(kwargs)
1143
 
        # init the superclass
1144
 
        Section.__init__(self, self, 0, self)
1145
 
        #
 
1209
        if _inspec:
 
1210
            options['list_values'] = False
 
1211
 
1146
1212
        defaults = OPTION_DEFAULTS.copy()
1147
 
        for entry in options.keys():
1148
 
            if entry not in defaults.keys():
1149
 
                raise TypeError, 'Unrecognised option "%s".' % entry
1150
1213
        # TODO: check the values too.
1151
 
        #
 
1214
        for entry in options:
 
1215
            if entry not in defaults:
 
1216
                raise TypeError('Unrecognised option "%s".' % entry)
 
1217
 
1152
1218
        # Add any explicit options to the defaults
1153
1219
        defaults.update(options)
1154
 
        #
1155
 
        # initialise a few variables
1156
 
        self.filename = None
1157
 
        self._errors = []
1158
 
        self.raise_errors = defaults['raise_errors']
1159
 
        self.interpolation = defaults['interpolation']
1160
 
        self.list_values = defaults['list_values']
1161
 
        self.create_empty = defaults['create_empty']
1162
 
        self.file_error = defaults['file_error']
1163
 
        self.stringify = defaults['stringify']
1164
 
        self.indent_type = defaults['indent_type']
1165
 
        self.encoding = defaults['encoding']
1166
 
        self.default_encoding = defaults['default_encoding']
1167
 
        self.BOM = False
1168
 
        self.newlines = None
1169
 
        self.write_empty_values = defaults['write_empty_values']
1170
 
        self.unrepr = defaults['unrepr']
1171
 
        #
1172
 
        self.initial_comment = []
1173
 
        self.final_comment = []
1174
 
        #
1175
 
        self._terminated = False
1176
 
        #
1177
 
        if isinstance(infile, StringTypes):
 
1220
        self._initialise(defaults)
 
1221
        configspec = defaults['configspec']
 
1222
        self._original_configspec = configspec
 
1223
        self._load(infile, configspec)
 
1224
 
 
1225
 
 
1226
    def _load(self, infile, configspec):
 
1227
        if isinstance(infile, basestring):
1178
1228
            self.filename = infile
1179
1229
            if os.path.isfile(infile):
1180
 
                infile = open(infile).read() or []
 
1230
                h = open(infile, 'rb')
 
1231
                infile = h.read() or []
 
1232
                h.close()
1181
1233
            elif self.file_error:
1182
1234
                # raise an error if the file doesn't exist
1183
 
                raise IOError, 'Config file not found: "%s".' % self.filename
 
1235
                raise IOError('Config file not found: "%s".' % self.filename)
1184
1236
            else:
1185
1237
                # file doesn't already exist
1186
1238
                if self.create_empty:
1187
1239
                    # this is a good test that the filename specified
1188
 
                    # isn't impossible - like on a non existent device
 
1240
                    # isn't impossible - like on a non-existent device
1189
1241
                    h = open(infile, 'w')
1190
1242
                    h.write('')
1191
1243
                    h.close()
1192
1244
                infile = []
 
1245
 
1193
1246
        elif isinstance(infile, (list, tuple)):
1194
1247
            infile = list(infile)
 
1248
 
1195
1249
        elif isinstance(infile, dict):
1196
1250
            # initialise self
1197
1251
            # the Section class handles creating subsections
1198
1252
            if isinstance(infile, ConfigObj):
1199
1253
                # get a copy of our ConfigObj
1200
1254
                infile = infile.dict()
 
1255
 
1201
1256
            for entry in infile:
1202
1257
                self[entry] = infile[entry]
1203
1258
            del self._errors
1204
 
            if defaults['configspec'] is not None:
1205
 
                self._handle_configspec(defaults['configspec'])
 
1259
 
 
1260
            if configspec is not None:
 
1261
                self._handle_configspec(configspec)
1206
1262
            else:
1207
1263
                self.configspec = None
1208
1264
            return
1209
 
        elif hasattr(infile, 'read'):
 
1265
 
 
1266
        elif getattr(infile, 'read', MISSING) is not MISSING:
1210
1267
            # This supports file like objects
1211
1268
            infile = infile.read() or []
1212
1269
            # needs splitting into lines - but needs doing *after* decoding
1213
1270
            # in case it's not an 8 bit encoding
1214
1271
        else:
1215
 
            raise TypeError, ('infile must be a filename,'
1216
 
                ' file like object, or list of lines.')
1217
 
        #
 
1272
            raise TypeError('infile must be a filename, file like object, or list of lines.')
 
1273
 
1218
1274
        if infile:
1219
1275
            # don't do it for the empty ConfigObj
1220
1276
            infile = self._handle_bom(infile)
1230
1286
                        self.newlines = end
1231
1287
                        break
1232
1288
                break
1233
 
            if infile[-1] and infile[-1] in ('\r', '\n', '\r\n'):
1234
 
                self._terminated = True
 
1289
 
1235
1290
            infile = [line.rstrip('\r\n') for line in infile]
1236
 
        #
 
1291
 
1237
1292
        self._parse(infile)
1238
1293
        # if we had any errors, now is the time to raise them
1239
1294
        if self._errors:
1240
1295
            info = "at line %s." % self._errors[0].line_number
1241
1296
            if len(self._errors) > 1:
1242
 
                msg = ("Parsing failed with several errors.\nFirst error %s" %
1243
 
                    info)
 
1297
                msg = "Parsing failed with several errors.\nFirst error %s" % info
1244
1298
                error = ConfigObjError(msg)
1245
1299
            else:
1246
1300
                error = self._errors[0]
1252
1306
            raise error
1253
1307
        # delete private attributes
1254
1308
        del self._errors
1255
 
        #
1256
 
        if defaults['configspec'] is None:
 
1309
 
 
1310
        if configspec is None:
1257
1311
            self.configspec = None
1258
1312
        else:
1259
 
            self._handle_configspec(defaults['configspec'])
1260
 
    
 
1313
            self._handle_configspec(configspec)
 
1314
 
 
1315
 
 
1316
    def _initialise(self, options=None):
 
1317
        if options is None:
 
1318
            options = OPTION_DEFAULTS
 
1319
 
 
1320
        # initialise a few variables
 
1321
        self.filename = None
 
1322
        self._errors = []
 
1323
        self.raise_errors = options['raise_errors']
 
1324
        self.interpolation = options['interpolation']
 
1325
        self.list_values = options['list_values']
 
1326
        self.create_empty = options['create_empty']
 
1327
        self.file_error = options['file_error']
 
1328
        self.stringify = options['stringify']
 
1329
        self.indent_type = options['indent_type']
 
1330
        self.encoding = options['encoding']
 
1331
        self.default_encoding = options['default_encoding']
 
1332
        self.BOM = False
 
1333
        self.newlines = None
 
1334
        self.write_empty_values = options['write_empty_values']
 
1335
        self.unrepr = options['unrepr']
 
1336
 
 
1337
        self.initial_comment = []
 
1338
        self.final_comment = []
 
1339
        self.configspec = None
 
1340
 
 
1341
        if self._inspec:
 
1342
            self.list_values = False
 
1343
 
 
1344
        # Clear section attributes as well
 
1345
        Section._initialise(self)
 
1346
 
 
1347
 
1261
1348
    def __repr__(self):
1262
 
        return 'ConfigObj({%s})' % ', '.join(
1263
 
            [('%s: %s' % (repr(key), repr(self[key]))) for key in
1264
 
            (self.scalars + self.sections)])
1265
 
    
 
1349
        return ('ConfigObj({%s})' %
 
1350
                ', '.join([('%s: %s' % (repr(key), repr(self[key])))
 
1351
                for key in (self.scalars + self.sections)]))
 
1352
 
 
1353
 
1266
1354
    def _handle_bom(self, infile):
1267
1355
        """
1268
1356
        Handle any BOM, and decode if necessary.
1269
 
        
 
1357
 
1270
1358
        If an encoding is specified, that *must* be used - but the BOM should
1271
1359
        still be removed (and the BOM attribute set).
1272
 
        
 
1360
 
1273
1361
        (If the encoding is wrongly specified, then a BOM for an alternative
1274
1362
        encoding won't be discovered or removed.)
1275
 
        
 
1363
 
1276
1364
        If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1277
1365
        removed. The BOM attribute will be set. UTF16 will be decoded to
1278
1366
        unicode.
1279
 
        
 
1367
 
1280
1368
        NOTE: This method must not be called with an empty ``infile``.
1281
 
        
 
1369
 
1282
1370
        Specifying the *wrong* encoding is likely to cause a
1283
1371
        ``UnicodeDecodeError``.
1284
 
        
 
1372
 
1285
1373
        ``infile`` must always be returned as a list of lines, but may be
1286
1374
        passed in as a single string.
1287
1375
        """
1291
1379
            # the encoding specified doesn't have one
1292
1380
            # just decode
1293
1381
            return self._decode(infile, self.encoding)
1294
 
        #
 
1382
 
1295
1383
        if isinstance(infile, (list, tuple)):
1296
1384
            line = infile[0]
1297
1385
        else:
1313
1401
                        ##self.BOM = True
1314
1402
                        # Don't need to remove BOM
1315
1403
                        return self._decode(infile, encoding)
1316
 
                #
 
1404
 
1317
1405
                # If we get this far, will *probably* raise a DecodeError
1318
1406
                # As it doesn't appear to start with a BOM
1319
1407
                return self._decode(infile, self.encoding)
1320
 
            #
 
1408
 
1321
1409
            # Must be UTF8
1322
1410
            BOM = BOM_SET[enc]
1323
1411
            if not line.startswith(BOM):
1324
1412
                return self._decode(infile, self.encoding)
1325
 
            #
 
1413
 
1326
1414
            newline = line[len(BOM):]
1327
 
            #
 
1415
 
1328
1416
            # BOM removed
1329
1417
            if isinstance(infile, (list, tuple)):
1330
1418
                infile[0] = newline
1332
1420
                infile = newline
1333
1421
            self.BOM = True
1334
1422
            return self._decode(infile, self.encoding)
1335
 
        #
 
1423
 
1336
1424
        # No encoding specified - so we need to check for UTF8/UTF16
1337
1425
        for BOM, (encoding, final_encoding) in BOMS.items():
1338
1426
            if not line.startswith(BOM):
1350
1438
                    else:
1351
1439
                        infile = newline
1352
1440
                    # UTF8 - don't decode
1353
 
                    if isinstance(infile, StringTypes):
 
1441
                    if isinstance(infile, basestring):
1354
1442
                        return infile.splitlines(True)
1355
1443
                    else:
1356
1444
                        return infile
1357
1445
                # UTF16 - have to decode
1358
1446
                return self._decode(infile, encoding)
1359
 
        #
 
1447
 
1360
1448
        # No BOM discovered and no encoding specified, just return
1361
 
        if isinstance(infile, StringTypes):
 
1449
        if isinstance(infile, basestring):
1362
1450
            # infile read from a file will be a single string
1363
1451
            return infile.splitlines(True)
1364
 
        else:
1365
 
            return infile
 
1452
        return infile
 
1453
 
1366
1454
 
1367
1455
    def _a_to_u(self, aString):
1368
1456
        """Decode ASCII strings to unicode if a self.encoding is specified."""
1371
1459
        else:
1372
1460
            return aString
1373
1461
 
 
1462
 
1374
1463
    def _decode(self, infile, encoding):
1375
1464
        """
1376
1465
        Decode infile to unicode. Using the specified encoding.
1377
 
        
 
1466
 
1378
1467
        if is a string, it also needs converting to a list.
1379
1468
        """
1380
 
        if isinstance(infile, StringTypes):
 
1469
        if isinstance(infile, basestring):
1381
1470
            # can't be unicode
1382
1471
            # NOTE: Could raise a ``UnicodeDecodeError``
1383
1472
            return infile.decode(encoding).splitlines(True)
1389
1478
                infile[i] = line.decode(encoding)
1390
1479
        return infile
1391
1480
 
 
1481
 
1392
1482
    def _decode_element(self, line):
1393
1483
        """Decode element to unicode if necessary."""
1394
1484
        if not self.encoding:
1397
1487
            return line.decode(self.default_encoding)
1398
1488
        return line
1399
1489
 
 
1490
 
1400
1491
    def _str(self, value):
1401
1492
        """
1402
1493
        Used by ``stringify`` within validate, to turn non-string values
1403
1494
        into strings.
1404
1495
        """
1405
 
        if not isinstance(value, StringTypes):
 
1496
        if not isinstance(value, basestring):
1406
1497
            return str(value)
1407
1498
        else:
1408
1499
            return value
1409
1500
 
 
1501
 
1410
1502
    def _parse(self, infile):
1411
1503
        """Actually parse the config file."""
1412
1504
        temp_list_values = self.list_values
1413
1505
        if self.unrepr:
1414
1506
            self.list_values = False
 
1507
 
1415
1508
        comment_list = []
1416
1509
        done_start = False
1417
1510
        this_section = self
1418
1511
        maxline = len(infile) - 1
1419
1512
        cur_index = -1
1420
1513
        reset_comment = False
 
1514
 
1421
1515
        while cur_index < maxline:
1422
1516
            if reset_comment:
1423
1517
                comment_list = []
1429
1523
                reset_comment = False
1430
1524
                comment_list.append(line)
1431
1525
                continue
 
1526
 
1432
1527
            if not done_start:
1433
1528
                # preserve initial comment
1434
1529
                self.initial_comment = comment_list
1435
1530
                comment_list = []
1436
1531
                done_start = True
 
1532
 
1437
1533
            reset_comment = True
1438
1534
            # first we check if it's a section marker
1439
1535
            mat = self._sectionmarker.match(line)
1440
1536
            if mat is not None:
1441
1537
                # is a section line
1442
 
                (indent, sect_open, sect_name, sect_close, comment) = (
1443
 
                    mat.groups())
 
1538
                (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1444
1539
                if indent and (self.indent_type is None):
1445
1540
                    self.indent_type = indent
1446
1541
                cur_depth = sect_open.count('[')
1447
1542
                if cur_depth != sect_close.count(']'):
1448
 
                    self._handle_error(
1449
 
                        "Cannot compute the section depth at line %s.",
1450
 
                        NestingError, infile, cur_index)
 
1543
                    self._handle_error("Cannot compute the section depth at line %s.",
 
1544
                                       NestingError, infile, cur_index)
1451
1545
                    continue
1452
 
                #
 
1546
 
1453
1547
                if cur_depth < this_section.depth:
1454
1548
                    # the new section is dropping back to a previous level
1455
1549
                    try:
1456
 
                        parent = self._match_depth(
1457
 
                            this_section,
1458
 
                            cur_depth).parent
 
1550
                        parent = self._match_depth(this_section,
 
1551
                                                   cur_depth).parent
1459
1552
                    except SyntaxError:
1460
 
                        self._handle_error(
1461
 
                            "Cannot compute nesting level at line %s.",
1462
 
                            NestingError, infile, cur_index)
 
1553
                        self._handle_error("Cannot compute nesting level at line %s.",
 
1554
                                           NestingError, infile, cur_index)
1463
1555
                        continue
1464
1556
                elif cur_depth == this_section.depth:
1465
1557
                    # the new section is a sibling of the current section
1468
1560
                    # the new section is a child the current section
1469
1561
                    parent = this_section
1470
1562
                else:
1471
 
                    self._handle_error(
1472
 
                        "Section too nested at line %s.",
1473
 
                        NestingError, infile, cur_index)
1474
 
                #
 
1563
                    self._handle_error("Section too nested at line %s.",
 
1564
                                       NestingError, infile, cur_index)
 
1565
 
1475
1566
                sect_name = self._unquote(sect_name)
1476
 
                if parent.has_key(sect_name):
1477
 
                    self._handle_error(
1478
 
                        'Duplicate section name at line %s.',
1479
 
                        DuplicateError, infile, cur_index)
 
1567
                if sect_name in parent:
 
1568
                    self._handle_error('Duplicate section name at line %s.',
 
1569
                                       DuplicateError, infile, cur_index)
1480
1570
                    continue
 
1571
 
1481
1572
                # create the new section
1482
1573
                this_section = Section(
1483
1574
                    parent,
1551
1642
                            continue
1552
1643
                #
1553
1644
                key = self._unquote(key)
1554
 
                if this_section.has_key(key):
 
1645
                if key in this_section:
1555
1646
                    self._handle_error(
1556
1647
                        'Duplicate keyword name at line %s.',
1557
1648
                        DuplicateError, infile, cur_index)
1567
1658
        if self.indent_type is None:
1568
1659
            # no indentation used, set the type accordingly
1569
1660
            self.indent_type = ''
1570
 
        #
1571
 
        if self._terminated:
1572
 
            comment_list.append('')
 
1661
 
1573
1662
        # preserve the final comment
1574
1663
        if not self and not self.initial_comment:
1575
1664
            self.initial_comment = comment_list
1577
1666
            self.final_comment = comment_list
1578
1667
        self.list_values = temp_list_values
1579
1668
 
 
1669
 
1580
1670
    def _match_depth(self, sect, depth):
1581
1671
        """
1582
1672
        Given a section and a depth level, walk back through the sections
1583
1673
        parents to see if the depth level matches a previous section.
1584
 
        
 
1674
 
1585
1675
        Return a reference to the right section,
1586
1676
        or raise a SyntaxError.
1587
1677
        """
1588
1678
        while depth < sect.depth:
1589
1679
            if sect is sect.parent:
1590
1680
                # we've reached the top level already
1591
 
                raise SyntaxError
 
1681
                raise SyntaxError()
1592
1682
            sect = sect.parent
1593
1683
        if sect.depth == depth:
1594
1684
            return sect
1595
1685
        # shouldn't get here
1596
 
        raise SyntaxError
 
1686
        raise SyntaxError()
 
1687
 
1597
1688
 
1598
1689
    def _handle_error(self, text, ErrorClass, infile, cur_index):
1599
1690
        """
1600
1691
        Handle an error according to the error settings.
1601
 
        
 
1692
 
1602
1693
        Either raise the error or store it.
1603
1694
        The error will have occured at ``cur_index``
1604
1695
        """
1613
1704
        # reraise when parsing has finished
1614
1705
        self._errors.append(error)
1615
1706
 
 
1707
 
1616
1708
    def _unquote(self, value):
1617
1709
        """Return an unquoted version of a value"""
1618
1710
        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1619
1711
            value = value[1:-1]
1620
1712
        return value
1621
1713
 
 
1714
 
1622
1715
    def _quote(self, value, multiline=True):
1623
1716
        """
1624
1717
        Return a safely quoted version of a value.
1625
 
        
 
1718
 
1626
1719
        Raise a ConfigObjError if the value cannot be safely quoted.
1627
1720
        If multiline is ``True`` (default) then use triple quotes
1628
1721
        if necessary.
1629
 
        
1630
 
        Don't quote values that don't need it.
1631
 
        Recursively quote members of a list and return a comma joined list.
1632
 
        Multiline is ``False`` for lists.
1633
 
        Obey list syntax for empty and single member lists.
1634
 
        
 
1722
 
 
1723
        * Don't quote values that don't need it.
 
1724
        * Recursively quote members of a list and return a comma joined list.
 
1725
        * Multiline is ``False`` for lists.
 
1726
        * Obey list syntax for empty and single member lists.
 
1727
 
1635
1728
        If ``list_values=False`` then the value is only quoted if it contains
1636
 
        a ``\n`` (is multiline).
1637
 
        
 
1729
        a ``\\n`` (is multiline) or '#'.
 
1730
 
1638
1731
        If ``write_empty_values`` is set, and the value is an empty string, it
1639
1732
        won't be quoted.
1640
1733
        """
1642
1735
            # Only if multiline is set, so that it is used for values not
1643
1736
            # keys, and not values that are part of a list
1644
1737
            return ''
 
1738
 
1645
1739
        if multiline and isinstance(value, (list, tuple)):
1646
1740
            if not value:
1647
1741
                return ','
1649
1743
                return self._quote(value[0], multiline=False) + ','
1650
1744
            return ', '.join([self._quote(val, multiline=False)
1651
1745
                for val in value])
1652
 
        if not isinstance(value, StringTypes):
 
1746
        if not isinstance(value, basestring):
1653
1747
            if self.stringify:
1654
1748
                value = str(value)
1655
1749
            else:
1656
 
                raise TypeError, 'Value "%s" is not a string.' % value
1657
 
        squot = "'%s'"
1658
 
        dquot = '"%s"'
1659
 
        noquot = "%s"
1660
 
        wspace_plus = ' \r\t\n\v\t\'"'
1661
 
        tsquot = '"""%s"""'
1662
 
        tdquot = "'''%s'''"
 
1750
                raise TypeError('Value "%s" is not a string.' % value)
 
1751
 
1663
1752
        if not value:
1664
1753
            return '""'
1665
 
        if (not self.list_values and '\n' not in value) or not (multiline and
1666
 
                ((("'" in value) and ('"' in value)) or ('\n' in value))):
 
1754
 
 
1755
        no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
 
1756
        need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
 
1757
        hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
 
1758
        check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
 
1759
 
 
1760
        if check_for_single:
1667
1761
            if not self.list_values:
1668
1762
                # we don't quote if ``list_values=False``
1669
1763
                quot = noquot
1670
1764
            # for normal values either single or double quotes will do
1671
1765
            elif '\n' in value:
1672
1766
                # will only happen if multiline is off - e.g. '\n' in key
1673
 
                raise ConfigObjError, ('Value "%s" cannot be safely quoted.' %
1674
 
                    value)
 
1767
                raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1675
1768
            elif ((value[0] not in wspace_plus) and
1676
1769
                    (value[-1] not in wspace_plus) and
1677
1770
                    (',' not in value)):
1678
1771
                quot = noquot
1679
1772
            else:
1680
 
                if ("'" in value) and ('"' in value):
1681
 
                    raise ConfigObjError, (
1682
 
                        'Value "%s" cannot be safely quoted.' % value)
1683
 
                elif '"' in value:
1684
 
                    quot = squot
1685
 
                else:
1686
 
                    quot = dquot
 
1773
                quot = self._get_single_quote(value)
1687
1774
        else:
1688
1775
            # if value has '\n' or "'" *and* '"', it will need triple quotes
1689
 
            if (value.find('"""') != -1) and (value.find("'''") != -1):
1690
 
                raise ConfigObjError, (
1691
 
                    'Value "%s" cannot be safely quoted.' % value)
1692
 
            if value.find('"""') == -1:
1693
 
                quot = tdquot
1694
 
            else:
1695
 
                quot = tsquot
 
1776
            quot = self._get_triple_quote(value)
 
1777
 
 
1778
        if quot == noquot and '#' in value and self.list_values:
 
1779
            quot = self._get_single_quote(value)
 
1780
 
1696
1781
        return quot % value
1697
1782
 
 
1783
 
 
1784
    def _get_single_quote(self, value):
 
1785
        if ("'" in value) and ('"' in value):
 
1786
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
 
1787
        elif '"' in value:
 
1788
            quot = squot
 
1789
        else:
 
1790
            quot = dquot
 
1791
        return quot
 
1792
 
 
1793
 
 
1794
    def _get_triple_quote(self, value):
 
1795
        if (value.find('"""') != -1) and (value.find("'''") != -1):
 
1796
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
 
1797
        # upstream version (up to version 4.7.2) has the bug with incorrect quoting;
 
1798
        # fixed in our copy based on the suggestion of ConfigObj's author
 
1799
        if value.find('"""') == -1:
 
1800
            quot = tsquot
 
1801
        else:
 
1802
            quot = tdquot
 
1803
        return quot
 
1804
 
 
1805
 
1698
1806
    def _handle_value(self, value):
1699
1807
        """
1700
1808
        Given a value string, unquote, remove comment,
1701
1809
        handle lists. (including empty and single member lists)
1702
1810
        """
 
1811
        if self._inspec:
 
1812
            # Parsing a configspec so don't handle comments
 
1813
            return (value, '')
1703
1814
        # do we look for lists in values ?
1704
1815
        if not self.list_values:
1705
1816
            mat = self._nolistvalue.match(value)
1706
1817
            if mat is None:
1707
 
                raise SyntaxError
 
1818
                raise SyntaxError()
1708
1819
            # NOTE: we don't unquote here
1709
1820
            return mat.groups()
1710
1821
        #
1712
1823
        if mat is None:
1713
1824
            # the value is badly constructed, probably badly quoted,
1714
1825
            # or an invalid list
1715
 
            raise SyntaxError
 
1826
            raise SyntaxError()
1716
1827
        (list_values, single, empty_list, comment) = mat.groups()
1717
1828
        if (list_values == '') and (single is None):
1718
1829
            # change this if you want to accept empty values
1719
 
            raise SyntaxError
 
1830
            raise SyntaxError()
1720
1831
        # NOTE: note there is no error handling from here if the regex
1721
1832
        # is wrong: then incorrect values will slip through
1722
1833
        if empty_list is not None:
1740
1851
            the_list += [single]
1741
1852
        return (the_list, comment)
1742
1853
 
 
1854
 
1743
1855
    def _multiline(self, value, infile, cur_index, maxline):
1744
1856
        """Extract the value, where we are in a multiline situation."""
1745
1857
        quot = value[:3]
1753
1865
            return retval
1754
1866
        elif newvalue.find(quot) != -1:
1755
1867
            # somehow the triple quote is missing
1756
 
            raise SyntaxError
 
1868
            raise SyntaxError()
1757
1869
        #
1758
1870
        while cur_index < maxline:
1759
1871
            cur_index += 1
1766
1878
                break
1767
1879
        else:
1768
1880
            # we've got to the end of the config, oops...
1769
 
            raise SyntaxError
 
1881
            raise SyntaxError()
1770
1882
        mat = multi_line.match(line)
1771
1883
        if mat is None:
1772
1884
            # a badly formed line
1773
 
            raise SyntaxError
 
1885
            raise SyntaxError()
1774
1886
        (value, comment) = mat.groups()
1775
1887
        return (newvalue + value, comment, cur_index)
1776
1888
 
 
1889
 
1777
1890
    def _handle_configspec(self, configspec):
1778
1891
        """Parse the configspec."""
1779
 
        # FIXME: Should we check that the configspec was created with the 
1780
 
        #   correct settings ? (i.e. ``list_values=False``)
 
1892
        # FIXME: Should we check that the configspec was created with the
 
1893
        #        correct settings ? (i.e. ``list_values=False``)
1781
1894
        if not isinstance(configspec, ConfigObj):
1782
1895
            try:
1783
 
                configspec = ConfigObj(
1784
 
                    configspec,
1785
 
                    raise_errors=True,
1786
 
                    file_error=True,
1787
 
                    list_values=False)
 
1896
                configspec = ConfigObj(configspec,
 
1897
                                       raise_errors=True,
 
1898
                                       file_error=True,
 
1899
                                       _inspec=True)
1788
1900
            except ConfigObjError, e:
1789
1901
                # FIXME: Should these errors have a reference
1790
 
                # to the already parsed ConfigObj ?
 
1902
                #        to the already parsed ConfigObj ?
1791
1903
                raise ConfigspecError('Parsing configspec failed: %s' % e)
1792
1904
            except IOError, e:
1793
1905
                raise IOError('Reading configspec failed: %s' % e)
1794
 
        self._set_configspec_value(configspec, self)
1795
 
 
1796
 
    def _set_configspec_value(self, configspec, section):
1797
 
        """Used to recursively set configspec values."""
1798
 
        if '__many__' in configspec.sections:
1799
 
            section.configspec['__many__'] = configspec['__many__']
1800
 
            if len(configspec.sections) > 1:
1801
 
                # FIXME: can we supply any useful information here ?
1802
 
                raise RepeatSectionError
1803
 
        if hasattr(configspec, 'initial_comment'):
1804
 
            section._configspec_initial_comment = configspec.initial_comment
1805
 
            section._configspec_final_comment = configspec.final_comment
1806
 
            section._configspec_encoding = configspec.encoding
1807
 
            section._configspec_BOM = configspec.BOM
1808
 
            section._configspec_newlines = configspec.newlines
1809
 
            section._configspec_indent_type = configspec.indent_type
1810
 
        for entry in configspec.scalars:
1811
 
            section._configspec_comments[entry] = configspec.comments[entry]
1812
 
            section._configspec_inline_comments[entry] = (
1813
 
                configspec.inline_comments[entry])
1814
 
            section.configspec[entry] = configspec[entry]
1815
 
            section._order.append(entry)
 
1906
 
 
1907
        self.configspec = configspec
 
1908
 
 
1909
 
 
1910
 
 
1911
    def _set_configspec(self, section, copy):
 
1912
        """
 
1913
        Called by validate. Handles setting the configspec on subsections
 
1914
        including sections to be validated by __many__
 
1915
        """
 
1916
        configspec = section.configspec
 
1917
        many = configspec.get('__many__')
 
1918
        if isinstance(many, dict):
 
1919
            for entry in section.sections:
 
1920
                if entry not in configspec:
 
1921
                    section[entry].configspec = many
 
1922
 
1816
1923
        for entry in configspec.sections:
1817
1924
            if entry == '__many__':
1818
1925
                continue
1819
 
            section._cs_section_comments[entry] = configspec.comments[entry]
1820
 
            section._cs_section_inline_comments[entry] = (
1821
 
                configspec.inline_comments[entry])
1822
 
            if not section.has_key(entry):
1823
 
                section[entry] = {}
1824
 
            self._set_configspec_value(configspec[entry], section[entry])
1825
 
 
1826
 
    def _handle_repeat(self, section, configspec):
1827
 
        """Dynamically assign configspec for repeated section."""
1828
 
        try:
1829
 
            section_keys = configspec.sections
1830
 
            scalar_keys = configspec.scalars
1831
 
        except AttributeError:
1832
 
            section_keys = [entry for entry in configspec 
1833
 
                                if isinstance(configspec[entry], dict)]
1834
 
            scalar_keys = [entry for entry in configspec 
1835
 
                                if not isinstance(configspec[entry], dict)]
1836
 
        if '__many__' in section_keys and len(section_keys) > 1:
1837
 
            # FIXME: can we supply any useful information here ?
1838
 
            raise RepeatSectionError
1839
 
        scalars = {}
1840
 
        sections = {}
1841
 
        for entry in scalar_keys:
1842
 
            val = configspec[entry]
1843
 
            scalars[entry] = val
1844
 
        for entry in section_keys:
1845
 
            val = configspec[entry]
1846
 
            if entry == '__many__':
1847
 
                scalars[entry] = val
1848
 
                continue
1849
 
            sections[entry] = val
1850
 
        #
1851
 
        section.configspec = scalars
1852
 
        for entry in sections:
1853
 
            if not section.has_key(entry):
1854
 
                section[entry] = {}
1855
 
            self._handle_repeat(section[entry], sections[entry])
 
1926
            if entry not in section:
 
1927
                section[entry] = {}
 
1928
                if copy:
 
1929
                    # copy comments
 
1930
                    section.comments[entry] = configspec.comments.get(entry, [])
 
1931
                    section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
 
1932
 
 
1933
            # Could be a scalar when we expect a section
 
1934
            if isinstance(section[entry], Section):
 
1935
                section[entry].configspec = configspec[entry]
 
1936
 
1856
1937
 
1857
1938
    def _write_line(self, indent_string, entry, this_entry, comment):
1858
1939
        """Write an individual line, for the write method"""
1861
1942
            val = self._decode_element(self._quote(this_entry))
1862
1943
        else:
1863
1944
            val = repr(this_entry)
1864
 
        return '%s%s%s%s%s' % (
1865
 
            indent_string,
1866
 
            self._decode_element(self._quote(entry, multiline=False)),
1867
 
            self._a_to_u(' = '),
1868
 
            val,
1869
 
            self._decode_element(comment))
 
1945
        return '%s%s%s%s%s' % (indent_string,
 
1946
                               self._decode_element(self._quote(entry, multiline=False)),
 
1947
                               self._a_to_u(' = '),
 
1948
                               val,
 
1949
                               self._decode_element(comment))
 
1950
 
1870
1951
 
1871
1952
    def _write_marker(self, indent_string, depth, entry, comment):
1872
1953
        """Write a section marker line"""
1873
 
        return '%s%s%s%s%s' % (
1874
 
            indent_string,
1875
 
            self._a_to_u('[' * depth),
1876
 
            self._quote(self._decode_element(entry), multiline=False),
1877
 
            self._a_to_u(']' * depth),
1878
 
            self._decode_element(comment))
 
1954
        return '%s%s%s%s%s' % (indent_string,
 
1955
                               self._a_to_u('[' * depth),
 
1956
                               self._quote(self._decode_element(entry), multiline=False),
 
1957
                               self._a_to_u(']' * depth),
 
1958
                               self._decode_element(comment))
 
1959
 
1879
1960
 
1880
1961
    def _handle_comment(self, comment):
1881
1962
        """Deal with a comment."""
1886
1967
            start += self._a_to_u(' # ')
1887
1968
        return (start + comment)
1888
1969
 
 
1970
 
1889
1971
    # Public methods
1890
1972
 
1891
1973
    def write(self, outfile=None, section=None):
1892
1974
        """
1893
1975
        Write the current ConfigObj as a file
1894
 
        
 
1976
 
1895
1977
        tekNico: FIXME: use StringIO instead of real files
1896
 
        
 
1978
 
1897
1979
        >>> filename = a.filename
1898
1980
        >>> a.filename = 'test.ini'
1899
1981
        >>> a.write()
1904
1986
        if self.indent_type is None:
1905
1987
            # this can be true if initialised from a dictionary
1906
1988
            self.indent_type = DEFAULT_INDENT_TYPE
1907
 
        #
 
1989
 
1908
1990
        out = []
1909
1991
        cs = self._a_to_u('#')
1910
1992
        csp = self._a_to_u('# ')
1918
2000
                if stripped_line and not stripped_line.startswith(cs):
1919
2001
                    line = csp + line
1920
2002
                out.append(line)
1921
 
        #
 
2003
 
1922
2004
        indent_string = self.indent_type * section.depth
1923
2005
        for entry in (section.scalars + section.sections):
1924
2006
            if entry in section.defaults:
1931
2013
                out.append(indent_string + comment_line)
1932
2014
            this_entry = section[entry]
1933
2015
            comment = self._handle_comment(section.inline_comments[entry])
1934
 
            #
 
2016
 
1935
2017
            if isinstance(this_entry, dict):
1936
2018
                # a section
1937
2019
                out.append(self._write_marker(
1946
2028
                    entry,
1947
2029
                    this_entry,
1948
2030
                    comment))
1949
 
        #
 
2031
 
1950
2032
        if section is self:
1951
2033
            for line in self.final_comment:
1952
2034
                line = self._decode_element(line)
1955
2037
                    line = csp + line
1956
2038
                out.append(line)
1957
2039
            self.interpolation = int_val
1958
 
        #
 
2040
 
1959
2041
        if section is not self:
1960
2042
            return out
1961
 
        #
 
2043
 
1962
2044
        if (self.filename is None) and (outfile is None):
1963
2045
            # output a list of lines
1964
2046
            # might need to encode
1972
2054
                    out.append('')
1973
2055
                out[0] = BOM_UTF8 + out[0]
1974
2056
            return out
1975
 
        #
 
2057
 
1976
2058
        # Turn the list to a string, joined with correct newlines
1977
 
        output = (self._a_to_u(self.newlines or os.linesep)
1978
 
            ).join(out)
 
2059
        newline = self.newlines or os.linesep
 
2060
        output = self._a_to_u(newline).join(out)
1979
2061
        if self.encoding:
1980
2062
            output = output.encode(self.encoding)
1981
 
        if (self.BOM and ((self.encoding is None) or
1982
 
            (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
 
2063
        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
1983
2064
            # Add the UTF8 BOM
1984
2065
            output = BOM_UTF8 + output
 
2066
 
 
2067
        if not output.endswith(newline):
 
2068
            output += newline
1985
2069
        if outfile is not None:
1986
2070
            outfile.write(output)
1987
2071
        else:
1989
2073
            h.write(output)
1990
2074
            h.close()
1991
2075
 
 
2076
 
1992
2077
    def validate(self, validator, preserve_errors=False, copy=False,
1993
 
        section=None):
 
2078
                 section=None):
1994
2079
        """
1995
2080
        Test the ConfigObj against a configspec.
1996
 
        
 
2081
 
1997
2082
        It uses the ``validator`` object from *validate.py*.
1998
 
        
 
2083
 
1999
2084
        To run ``validate`` on the current ConfigObj, call: ::
2000
 
        
 
2085
 
2001
2086
            test = config.validate(validator)
2002
 
        
 
2087
 
2003
2088
        (Normally having previously passed in the configspec when the ConfigObj
2004
2089
        was created - you can dynamically assign a dictionary of checks to the
2005
2090
        ``configspec`` attribute of a section though).
2006
 
        
 
2091
 
2007
2092
        It returns ``True`` if everything passes, or a dictionary of
2008
2093
        pass/fails (True/False). If every member of a subsection passes, it
2009
2094
        will just have the value ``True``. (It also returns ``False`` if all
2010
2095
        members fail).
2011
 
        
 
2096
 
2012
2097
        In addition, it converts the values from strings to their native
2013
2098
        types if their checks pass (and ``stringify`` is set).
2014
 
        
 
2099
 
2015
2100
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2016
2101
        of a marking a fail with a ``False``, it will preserve the actual
2017
2102
        exception object. This can contain info about the reason for failure.
2018
 
        For example the ``VdtValueTooSmallError`` indeicates that the value
 
2103
        For example the ``VdtValueTooSmallError`` indicates that the value
2019
2104
        supplied was too small. If a value (or section) is missing it will
2020
2105
        still be marked as ``False``.
2021
 
        
 
2106
 
2022
2107
        You must have the validate module to use ``preserve_errors=True``.
2023
 
        
 
2108
 
2024
2109
        You can then use the ``flatten_errors`` function to turn your nested
2025
2110
        results dictionary into a flattened list of failures - useful for
2026
2111
        displaying meaningful error messages.
2027
2112
        """
2028
2113
        if section is None:
2029
2114
            if self.configspec is None:
2030
 
                raise ValueError, 'No configspec supplied.'
 
2115
                raise ValueError('No configspec supplied.')
2031
2116
            if preserve_errors:
2032
 
                if VdtMissingValue is None:
2033
 
                    raise ImportError('Missing validate module.')
 
2117
                # We do this once to remove a top level dependency on the validate module
 
2118
                # Which makes importing configobj faster
 
2119
                from validate import VdtMissingValue
 
2120
                self._vdtMissingValue = VdtMissingValue
 
2121
 
2034
2122
            section = self
2035
 
        #
2036
 
        spec_section = section.configspec
2037
 
        if copy and hasattr(section, '_configspec_initial_comment'):
2038
 
            section.initial_comment = section._configspec_initial_comment
2039
 
            section.final_comment = section._configspec_final_comment
2040
 
            section.encoding = section._configspec_encoding
2041
 
            section.BOM = section._configspec_BOM
2042
 
            section.newlines = section._configspec_newlines
2043
 
            section.indent_type = section._configspec_indent_type
2044
 
        if '__many__' in section.configspec:
2045
 
            many = spec_section['__many__']
2046
 
            # dynamically assign the configspecs
2047
 
            # for the sections below
2048
 
            for entry in section.sections:
2049
 
                self._handle_repeat(section[entry], many)
2050
 
        #
2051
 
        out = {}
2052
 
        ret_true = True
2053
 
        ret_false = True
2054
 
        order = [k for k in section._order if k in spec_section]
2055
 
        order += [k for k in spec_section if k not in order]
2056
 
        for entry in order:
2057
 
            if entry == '__many__':
2058
 
                continue
2059
 
            if (not entry in section.scalars) or (entry in section.defaults):
2060
 
                # missing entries
2061
 
                # or entries from defaults
2062
 
                missing = True
2063
 
                val = None
2064
 
                if copy and not entry in section.scalars:
2065
 
                    # copy comments
2066
 
                    section.comments[entry] = (
2067
 
                        section._configspec_comments.get(entry, []))
2068
 
                    section.inline_comments[entry] = (
2069
 
                        section._configspec_inline_comments.get(entry, ''))
2070
 
                #
2071
 
            else:
2072
 
                missing = False
2073
 
                val = section[entry]
 
2123
 
 
2124
            if copy:
 
2125
                section.initial_comment = section.configspec.initial_comment
 
2126
                section.final_comment = section.configspec.final_comment
 
2127
                section.encoding = section.configspec.encoding
 
2128
                section.BOM = section.configspec.BOM
 
2129
                section.newlines = section.configspec.newlines
 
2130
                section.indent_type = section.configspec.indent_type
 
2131
 
 
2132
        #
 
2133
        configspec = section.configspec
 
2134
        self._set_configspec(section, copy)
 
2135
 
 
2136
        def validate_entry(entry, spec, val, missing, ret_true, ret_false):
2074
2137
            try:
2075
 
                check = validator.check(spec_section[entry],
 
2138
                check = validator.check(spec,
2076
2139
                                        val,
2077
2140
                                        missing=missing
2078
2141
                                        )
2079
2142
            except validator.baseErrorClass, e:
2080
 
                if not preserve_errors or isinstance(e, VdtMissingValue):
 
2143
                if not preserve_errors or isinstance(e, self._vdtMissingValue):
2081
2144
                    out[entry] = False
2082
2145
                else:
2083
2146
                    # preserve the error
2085
2148
                    ret_false = False
2086
2149
                ret_true = False
2087
2150
            else:
 
2151
                try:
 
2152
                    section.default_values.pop(entry, None)
 
2153
                except AttributeError:
 
2154
                    # For Python 2.2 compatibility
 
2155
                    try:
 
2156
                        del section.default_values[entry]
 
2157
                    except KeyError:
 
2158
                        pass
 
2159
 
 
2160
                try:
 
2161
                    section.default_values[entry] = validator.get_default_value(configspec[entry])
 
2162
                except (KeyError, AttributeError):
 
2163
                    # No default or validator has no 'get_default_value' (e.g. SimpleVal)
 
2164
                    pass
 
2165
 
2088
2166
                ret_false = False
2089
2167
                out[entry] = True
2090
2168
                if self.stringify or missing:
2103
2181
                        section[entry] = check
2104
2182
                if not copy and missing and entry not in section.defaults:
2105
2183
                    section.defaults.append(entry)
 
2184
            return ret_true, ret_false
 
2185
 
2106
2186
        #
 
2187
        out = {}
 
2188
        ret_true = True
 
2189
        ret_false = True
 
2190
 
 
2191
        unvalidated = [k for k in section.scalars if k not in configspec]
 
2192
        incorrect_sections = [k for k in configspec.sections if k in section.scalars]
 
2193
        incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
 
2194
 
 
2195
        for entry in configspec.scalars:
 
2196
            if entry in ('__many__', '___many___'):
 
2197
                # reserved names
 
2198
                continue
 
2199
 
 
2200
            if (not entry in section.scalars) or (entry in section.defaults):
 
2201
                # missing entries
 
2202
                # or entries from defaults
 
2203
                missing = True
 
2204
                val = None
 
2205
                if copy and not entry in section.scalars:
 
2206
                    # copy comments
 
2207
                    section.comments[entry] = (
 
2208
                        configspec.comments.get(entry, []))
 
2209
                    section.inline_comments[entry] = (
 
2210
                        configspec.inline_comments.get(entry, ''))
 
2211
                #
 
2212
            else:
 
2213
                missing = False
 
2214
                val = section[entry]
 
2215
 
 
2216
            ret_true, ret_false = validate_entry(entry, configspec[entry], val,
 
2217
                                                 missing, ret_true, ret_false)
 
2218
 
 
2219
        many = None
 
2220
        if '__many__' in configspec.scalars:
 
2221
            many = configspec['__many__']
 
2222
        elif '___many___' in configspec.scalars:
 
2223
            many = configspec['___many___']
 
2224
 
 
2225
        if many is not None:
 
2226
            for entry in unvalidated:
 
2227
                val = section[entry]
 
2228
                ret_true, ret_false = validate_entry(entry, many, val, False,
 
2229
                                                     ret_true, ret_false)
 
2230
 
 
2231
        for entry in incorrect_scalars:
 
2232
            ret_true = False
 
2233
            if not preserve_errors:
 
2234
                out[entry] = False
 
2235
            else:
 
2236
                ret_false = False
 
2237
                msg = 'Value %r was provided as a section' % entry
 
2238
                out[entry] = validator.baseErrorClass(msg)
 
2239
        for entry in incorrect_sections:
 
2240
            ret_true = False
 
2241
            if not preserve_errors:
 
2242
                out[entry] = False
 
2243
            else:
 
2244
                ret_false = False
 
2245
                msg = 'Section %r was provided as a single value' % entry
 
2246
                out[entry] = validator.baseErrorClass(msg)
 
2247
 
2107
2248
        # Missing sections will have been created as empty ones when the
2108
2249
        # configspec was read.
2109
2250
        for entry in section.sections:
2110
2251
            # FIXME: this means DEFAULT is not copied in copy mode
2111
2252
            if section is self and entry == 'DEFAULT':
2112
2253
                continue
 
2254
            if section[entry].configspec is None:
 
2255
                continue
2113
2256
            if copy:
2114
 
                section.comments[entry] = section._cs_section_comments[entry]
2115
 
                section.inline_comments[entry] = (
2116
 
                    section._cs_section_inline_comments[entry])
2117
 
            check = self.validate(validator, preserve_errors=preserve_errors,
2118
 
                copy=copy, section=section[entry])
 
2257
                section.comments[entry] = configspec.comments.get(entry, [])
 
2258
                section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
 
2259
            check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
2119
2260
            out[entry] = check
2120
2261
            if check == False:
2121
2262
                ret_true = False
2129
2270
            return True
2130
2271
        elif ret_false:
2131
2272
            return False
2132
 
        else:
2133
 
            return out
 
2273
        return out
 
2274
 
 
2275
 
 
2276
    def reset(self):
 
2277
        """Clear ConfigObj instance and restore to 'freshly created' state."""
 
2278
        self.clear()
 
2279
        self._initialise()
 
2280
        # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
 
2281
        #        requires an empty dictionary
 
2282
        self.configspec = None
 
2283
        # Just to be sure ;-)
 
2284
        self._original_configspec = None
 
2285
 
 
2286
 
 
2287
    def reload(self):
 
2288
        """
 
2289
        Reload a ConfigObj from file.
 
2290
 
 
2291
        This method raises a ``ReloadError`` if the ConfigObj doesn't have
 
2292
        a filename attribute pointing to a file.
 
2293
        """
 
2294
        if not isinstance(self.filename, basestring):
 
2295
            raise ReloadError()
 
2296
 
 
2297
        filename = self.filename
 
2298
        current_options = {}
 
2299
        for entry in OPTION_DEFAULTS:
 
2300
            if entry == 'configspec':
 
2301
                continue
 
2302
            current_options[entry] = getattr(self, entry)
 
2303
 
 
2304
        configspec = self._original_configspec
 
2305
        current_options['configspec'] = configspec
 
2306
 
 
2307
        self.clear()
 
2308
        self._initialise(current_options)
 
2309
        self._load(filename, configspec)
 
2310
 
 
2311
 
2134
2312
 
2135
2313
class SimpleVal(object):
2136
2314
    """
2137
2315
    A simple validator.
2138
2316
    Can be used to check that all members expected are present.
2139
 
    
 
2317
 
2140
2318
    To use it, provide a configspec with all your members in (the value given
2141
2319
    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2142
2320
    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2143
2321
    members are present, or a dictionary with True/False meaning
2144
2322
    present/missing. (Whole missing sections will be replaced with ``False``)
2145
2323
    """
2146
 
    
 
2324
 
2147
2325
    def __init__(self):
2148
2326
        self.baseErrorClass = ConfigObjError
2149
 
    
 
2327
 
2150
2328
    def check(self, check, member, missing=False):
2151
2329
        """A dummy check method, always returns the value unchanged."""
2152
2330
        if missing:
2153
 
            raise self.baseErrorClass
 
2331
            raise self.baseErrorClass()
2154
2332
        return member
2155
2333
 
 
2334
 
2156
2335
# Check / processing functions for options
2157
2336
def flatten_errors(cfg, res, levels=None, results=None):
2158
2337
    """
2159
2338
    An example function that will turn a nested dictionary of results
2160
2339
    (as returned by ``ConfigObj.validate``) into a flat list.
2161
 
    
 
2340
 
2162
2341
    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2163
2342
    dictionary returned by ``validate``.
2164
 
    
 
2343
 
2165
2344
    (This is a recursive function, so you shouldn't use the ``levels`` or
2166
 
    ``results`` arguments - they are used by the function.
2167
 
    
 
2345
    ``results`` arguments - they are used by the function.)
 
2346
 
2168
2347
    Returns a list of keys that failed. Each member of the list is a tuple :
 
2348
 
2169
2349
    ::
2170
 
    
 
2350
 
2171
2351
        ([list of sections...], key, result)
2172
 
    
 
2352
 
2173
2353
    If ``validate`` was called with ``preserve_errors=False`` (the default)
2174
2354
    then ``result`` will always be ``False``.
2175
2355
 
2176
2356
    *list of sections* is a flattened list of sections that the key was found
2177
2357
    in.
2178
 
    
2179
 
    If the section was missing then key will be ``None``.
2180
 
    
 
2358
 
 
2359
    If the section was missing (or a section was expected and a scalar provided
 
2360
    - or vice-versa) then key will be ``None``.
 
2361
 
2181
2362
    If the value (or section) was missing then ``result`` will be ``False``.
2182
 
    
 
2363
 
2183
2364
    If ``validate`` was called with ``preserve_errors=True`` and a value
2184
2365
    was present, but failed the check, then ``result`` will be the exception
2185
2366
    object returned. You can use this as a string that describes the failure.
2186
 
    
 
2367
 
2187
2368
    For example *The value "3" is of the wrong type*.
2188
 
    
 
2369
 
2189
2370
    >>> import validate
2190
2371
    >>> vtor = validate.Validator()
2191
2372
    >>> my_ini = '''
2255
2436
        results = []
2256
2437
    if res is True:
2257
2438
        return results
2258
 
    if res is False:
2259
 
        results.append((levels[:], None, False))
 
2439
    if res is False or isinstance(res, Exception):
 
2440
        results.append((levels[:], None, res))
2260
2441
        if levels:
2261
2442
            levels.pop()
2262
2443
        return results
2276
2457
    #
2277
2458
    return results
2278
2459
 
 
2460
 
2279
2461
"""*A programming language is a medium of expression.* - Paul Graham"""