~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Vincent Ladeuil
  • Date: 2007-12-21 12:20:33 UTC
  • mto: (3146.3.1 179368) (3156.2.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 3158.
  • Revision ID: v.ladeuil+lp@free.fr-20071221122033-42bc21re0zj4kqbg
Merge back test_http_implementations.pc into test_http.py.

* bzrlib/tests/test_http.py: 
Merge test_http_implementations.py now that we have rewritten
load_tests. That should reduce the noise in the final proposed
patch.

* bzrlib/tests/http_server.py:
(TestingHTTPRequestHandler.log_message): Ghaaa, don't over spell-check.

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