~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-03-16 16:58:03 UTC
  • mfrom: (3224.3.1 news-typo)
  • Revision ID: pqm@pqm.ubuntu.com-20080316165803-tisoc9mpob9z544o
(Matt Nordhoff) Trivial NEWS typo fix

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-2008 Michael Foord, Nicola Larosa
4
4
# E-mail: fuzzyman AT voidspace DOT org DOT uk
5
5
#         nico AT tekNico DOT net
6
6
 
16
16
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
17
17
# Comments, suggestions and bug reports welcome.
18
18
 
19
 
 
20
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:
100
97
squot = "'%s'"
101
98
dquot = '"%s"'
102
99
noquot = "%s"
103
 
wspace_plus = ' \r\n\v\t\'"'
 
100
wspace_plus = ' \r\t\n\v\t\'"'
104
101
tsquot = '"""%s"""'
105
102
tdquot = "'''%s'''"
106
103
 
114
111
            i += 1
115
112
            yield i, item
116
113
 
117
 
# Sentinel for use in getattr calls to replace hasattr
118
 
MISSING = object()
119
 
 
120
 
__version__ = '4.6.0'
 
114
try:
 
115
    True, False
 
116
except NameError:
 
117
    True, False = 1, 0
 
118
 
 
119
 
 
120
__version__ = '4.5.2'
121
121
 
122
122
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
123
123
 
180
180
 
181
181
 
182
182
class Builder(object):
183
 
 
 
183
    
184
184
    def build(self, o):
185
185
        m = getattr(self, 'build_' + o.__class__.__name__, None)
186
186
        if m is None:
187
187
            raise UnknownType(o.__class__.__name__)
188
188
        return m(o)
189
 
 
 
189
    
190
190
    def build_List(self, o):
191
191
        return map(self.build, o.getChildren())
192
 
 
 
192
    
193
193
    def build_Const(self, o):
194
194
        return o.value
195
 
 
 
195
    
196
196
    def build_Dict(self, o):
197
197
        d = {}
198
198
        i = iter(map(self.build, o.getChildren()))
199
199
        for el in i:
200
200
            d[el] = i.next()
201
201
        return d
202
 
 
 
202
    
203
203
    def build_Tuple(self, o):
204
204
        return tuple(self.build_List(o))
205
 
 
 
205
    
206
206
    def build_Name(self, o):
207
207
        if o.name == 'None':
208
208
            return None
210
210
            return True
211
211
        if o.name == 'False':
212
212
            return False
213
 
 
 
213
        
214
214
        # An undefined Name
215
215
        raise UnknownType('Undefined Name')
216
 
 
 
216
    
217
217
    def build_Add(self, o):
218
218
        real, imag = map(self.build_Const, o.getChildren())
219
219
        try:
223
223
        if not isinstance(imag, complex) or imag.real != 0.0:
224
224
            raise UnknownType('Add')
225
225
        return real+imag
226
 
 
 
226
    
227
227
    def build_Getattr(self, o):
228
228
        parent = self.build(o.expr)
229
229
        return getattr(parent, o.attrname)
230
 
 
 
230
    
231
231
    def build_UnarySub(self, o):
232
232
        return -self.build_Const(o.getChildren()[0])
233
 
 
 
233
    
234
234
    def build_UnaryAdd(self, o):
235
235
        return self.build_Const(o.getChildren()[0])
236
236
 
253
253
    def __init__(self, message='', line_number=None, line=''):
254
254
        self.line = line
255
255
        self.line_number = line_number
 
256
        self.message = message
256
257
        SyntaxError.__init__(self, message)
257
258
 
258
259
 
353
354
            This is similar to a depth-first-search algorithm.
354
355
            """
355
356
            # Have we been here already?
356
 
            if (key, section.name) in backtrail:
 
357
            if backtrail.has_key((key, section.name)):
357
358
                # Yes - infinite loop detected
358
359
                raise InterpolationLoopError(key)
359
360
            # Place a marker on our backtrail so we won't come back here again
441
442
        (e.g., if we interpolated "$$" and returned "$").
442
443
        """
443
444
        raise NotImplementedError()
444
 
 
 
445
    
445
446
 
446
447
 
447
448
class ConfigParserInterpolation(InterpolationEngine):
486
487
}
487
488
 
488
489
 
489
 
def __newobj__(cls, *args):
490
 
    # Hack for pickle
491
 
    return cls.__new__(cls, *args)
492
490
 
493
491
class Section(dict):
494
492
    """
495
493
    A dictionary-like object that represents a section in a config file.
496
 
 
 
494
    
497
495
    It does string interpolation if the 'interpolation' attribute
498
496
    of the 'main' object is set to True.
499
 
 
 
497
    
500
498
    Interpolation is tried first from this object, then from the 'DEFAULT'
501
499
    section of this object, next from the parent and its 'DEFAULT' section,
502
500
    and so on until the main object is reached.
503
 
 
 
501
    
504
502
    A Section will behave like an ordered dictionary - following the
505
503
    order of the ``scalars`` and ``sections`` attributes.
506
504
    You can use this to change the order of members.
507
 
 
 
505
    
508
506
    Iteration follows the order: scalars, then sections.
509
507
    """
510
508
 
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
509
    def __init__(self, parent, depth, main, indict=None, name=None):
522
510
        """
523
511
        * parent is the section above
542
530
        # (rather than just passing to ``dict.__init__``)
543
531
        for entry, value in indict.iteritems():
544
532
            self[entry] = value
545
 
 
546
 
 
 
533
            
 
534
            
547
535
    def _initialise(self):
548
536
        # the sequence of scalar values in this Section
549
537
        self.scalars = []
552
540
        # for comments :-)
553
541
        self.comments = {}
554
542
        self.inline_comments = {}
555
 
        # the configspec
556
 
        self.configspec = None
 
543
        # for the configspec
 
544
        self.configspec = {}
 
545
        self._order = []
 
546
        self._configspec_comments = {}
 
547
        self._configspec_inline_comments = {}
 
548
        self._cs_section_comments = {}
 
549
        self._cs_section_inline_comments = {}
557
550
        # for defaults
558
551
        self.defaults = []
559
552
        self.default_values = {}
585
578
    def __getitem__(self, key):
586
579
        """Fetch the item and do string interpolation."""
587
580
        val = dict.__getitem__(self, key)
588
 
        if self.main.interpolation and isinstance(val, basestring):
 
581
        if self.main.interpolation and isinstance(val, StringTypes):
589
582
            return self._interpolate(key, val)
590
583
        return val
591
584
 
593
586
    def __setitem__(self, key, value, unrepr=False):
594
587
        """
595
588
        Correctly set a value.
596
 
 
 
589
        
597
590
        Making dictionary values Section instances.
598
591
        (We have to special case 'Section' instances - which are also dicts)
599
 
 
 
592
        
600
593
        Keys must be strings.
601
594
        Values need only be strings (or lists of strings) if
602
595
        ``main.stringify`` is set.
603
 
 
604
 
        ``unrepr`` must be set when setting a value to a dictionary, without
 
596
        
 
597
        `unrepr`` must be set when setting a value to a dictionary, without
605
598
        creating a new sub-section.
606
599
        """
607
 
        if not isinstance(key, basestring):
 
600
        if not isinstance(key, StringTypes):
608
601
            raise ValueError('The key "%s" is not a string.' % key)
609
 
 
 
602
        
610
603
        # add the comment
611
 
        if key not in self.comments:
 
604
        if not self.comments.has_key(key):
612
605
            self.comments[key] = []
613
606
            self.inline_comments[key] = ''
614
607
        # remove the entry from defaults
616
609
            self.defaults.remove(key)
617
610
        #
618
611
        if isinstance(value, Section):
619
 
            if key not in self:
 
612
            if not self.has_key(key):
620
613
                self.sections.append(key)
621
614
            dict.__setitem__(self, key, value)
622
615
        elif isinstance(value, dict) and not unrepr:
623
616
            # First create the new depth level,
624
617
            # then create the section
625
 
            if key not in self:
 
618
            if not self.has_key(key):
626
619
                self.sections.append(key)
627
620
            new_depth = self.depth + 1
628
621
            dict.__setitem__(
635
628
                    indict=value,
636
629
                    name=key))
637
630
        else:
638
 
            if key not in self:
 
631
            if not self.has_key(key):
639
632
                self.scalars.append(key)
640
633
            if not self.main.stringify:
641
 
                if isinstance(value, basestring):
 
634
                if isinstance(value, StringTypes):
642
635
                    pass
643
636
                elif isinstance(value, (list, tuple)):
644
637
                    for entry in value:
645
 
                        if not isinstance(entry, basestring):
 
638
                        if not isinstance(entry, StringTypes):
646
639
                            raise TypeError('Value is not a string "%s".' % entry)
647
640
                else:
648
641
                    raise TypeError('Value is not a string "%s".' % value)
690
683
            del self.comments[key]
691
684
            del self.inline_comments[key]
692
685
            self.sections.remove(key)
693
 
        if self.main.interpolation and isinstance(val, basestring):
 
686
        if self.main.interpolation and isinstance(val, StringTypes):
694
687
            return self._interpolate(key, val)
695
688
        return val
696
689
 
710
703
        """
711
704
        A version of clear that also affects scalars/sections
712
705
        Also clears comments and configspec.
713
 
 
 
706
        
714
707
        Leaves other attributes alone :
715
708
            depth/main/parent are not affected
716
709
        """
719
712
        self.sections = []
720
713
        self.comments = {}
721
714
        self.inline_comments = {}
722
 
        self.configspec = None
 
715
        self.configspec = {}
723
716
 
724
717
 
725
718
    def setdefault(self, key, default=None):
777
770
    def dict(self):
778
771
        """
779
772
        Return a deepcopy of self as a dictionary.
780
 
 
 
773
        
781
774
        All members that are ``Section`` instances are recursively turned to
782
775
        ordinary dictionaries - by calling their ``dict`` method.
783
 
 
 
776
        
784
777
        >>> n = a.dict()
785
778
        >>> n == a
786
779
        1
805
798
    def merge(self, indict):
806
799
        """
807
800
        A recursive update - useful for merging config files.
808
 
 
 
801
        
809
802
        >>> a = '''[section1]
810
803
        ...     option1 = True
811
804
        ...     [[subsection]]
819
812
        >>> c2 = ConfigObj(a)
820
813
        >>> c2.merge(c1)
821
814
        >>> c2
822
 
        ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
 
815
        {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
823
816
        """
824
817
        for key, val in indict.items():
825
818
            if (key in self and isinstance(self[key], dict) and
826
819
                                isinstance(val, dict)):
827
820
                self[key].merge(val)
828
 
            else:
 
821
            else:   
829
822
                self[key] = val
830
823
 
831
824
 
832
825
    def rename(self, oldkey, newkey):
833
826
        """
834
827
        Change a keyname to another, without changing position in sequence.
835
 
 
 
828
        
836
829
        Implemented so that transformations can be made on keys,
837
830
        as well as on values. (used by encode and decode)
838
 
 
 
831
        
839
832
        Also renames comments.
840
833
        """
841
834
        if oldkey in self.scalars:
863
856
            call_on_sections=False, **keywargs):
864
857
        """
865
858
        Walk every member and call a function on the keyword and value.
866
 
 
 
859
        
867
860
        Return a dictionary of the return values
868
 
 
 
861
        
869
862
        If the function raises an exception, raise the errror
870
863
        unless ``raise_errors=False``, in which case set the return value to
871
864
        ``False``.
872
 
 
 
865
        
873
866
        Any unrecognised keyword arguments you pass to walk, will be pased on
874
867
        to the function you pass in.
875
 
 
 
868
        
876
869
        Note: if ``call_on_sections`` is ``True`` then - on encountering a
877
870
        subsection, *first* the function is called for the *whole* subsection,
878
871
        and then recurses into it's members. This means your function must be
879
872
        able to handle strings, dictionaries and lists. This allows you
880
873
        to change the key of subsections as well as for ordinary members. The
881
874
        return value when called on the whole subsection has to be discarded.
882
 
 
 
875
        
883
876
        See  the encode and decode methods for examples, including functions.
884
 
 
885
 
        .. admonition:: caution
886
 
 
 
877
        
 
878
        .. caution::
 
879
        
887
880
            You can use ``walk`` to transform the names of members of a section
888
881
            but you mustn't add or delete members.
889
 
 
 
882
        
890
883
        >>> config = '''[XXXXsection]
891
884
        ... XXXXkey = XXXXvalue'''.splitlines()
892
885
        >>> cfg = ConfigObj(config)
893
886
        >>> cfg
894
 
        ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
 
887
        {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
895
888
        >>> def transform(section, key):
896
889
        ...     val = section[key]
897
890
        ...     newkey = key.replace('XXXX', 'CLIENT1')
904
897
        >>> cfg.walk(transform, call_on_sections=True)
905
898
        {'CLIENT1section': {'CLIENT1key': None}}
906
899
        >>> cfg
907
 
        ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
 
900
        {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
908
901
        """
909
902
        out = {}
910
903
        # scalars first
944
937
        return out
945
938
 
946
939
 
 
940
    def decode(self, encoding):
 
941
        """
 
942
        Decode all strings and values to unicode, using the specified encoding.
 
943
        
 
944
        Works with subsections and list values.
 
945
        
 
946
        Uses the ``walk`` method.
 
947
        
 
948
        Testing ``encode`` and ``decode``.
 
949
        >>> m = ConfigObj(a)
 
950
        >>> m.decode('ascii')
 
951
        >>> def testuni(val):
 
952
        ...     for entry in val:
 
953
        ...         if not isinstance(entry, unicode):
 
954
        ...             print >> sys.stderr, type(entry)
 
955
        ...             raise AssertionError, 'decode failed.'
 
956
        ...         if isinstance(val[entry], dict):
 
957
        ...             testuni(val[entry])
 
958
        ...         elif not isinstance(val[entry], unicode):
 
959
        ...             raise AssertionError, 'decode failed.'
 
960
        >>> testuni(m)
 
961
        >>> m.encode('ascii')
 
962
        >>> a == m
 
963
        1
 
964
        """
 
965
        warn('use of ``decode`` is deprecated.', DeprecationWarning)
 
966
        def decode(section, key, encoding=encoding, warn=True):
 
967
            """ """
 
968
            val = section[key]
 
969
            if isinstance(val, (list, tuple)):
 
970
                newval = []
 
971
                for entry in val:
 
972
                    newval.append(entry.decode(encoding))
 
973
            elif isinstance(val, dict):
 
974
                newval = val
 
975
            else:
 
976
                newval = val.decode(encoding)
 
977
            newkey = key.decode(encoding)
 
978
            section.rename(key, newkey)
 
979
            section[newkey] = newval
 
980
        # using ``call_on_sections`` allows us to modify section names
 
981
        self.walk(decode, call_on_sections=True)
 
982
 
 
983
 
 
984
    def encode(self, encoding):
 
985
        """
 
986
        Encode all strings and values from unicode,
 
987
        using the specified encoding.
 
988
        
 
989
        Works with subsections and list values.
 
990
        Uses the ``walk`` method.
 
991
        """
 
992
        warn('use of ``encode`` is deprecated.', DeprecationWarning)
 
993
        def encode(section, key, encoding=encoding):
 
994
            """ """
 
995
            val = section[key]
 
996
            if isinstance(val, (list, tuple)):
 
997
                newval = []
 
998
                for entry in val:
 
999
                    newval.append(entry.encode(encoding))
 
1000
            elif isinstance(val, dict):
 
1001
                newval = val
 
1002
            else:
 
1003
                newval = val.encode(encoding)
 
1004
            newkey = key.encode(encoding)
 
1005
            section.rename(key, newkey)
 
1006
            section[newkey] = newval
 
1007
        self.walk(encode, call_on_sections=True)
 
1008
 
 
1009
 
 
1010
    def istrue(self, key):
 
1011
        """A deprecated version of ``as_bool``."""
 
1012
        warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
 
1013
                'instead.', DeprecationWarning)
 
1014
        return self.as_bool(key)
 
1015
 
 
1016
 
947
1017
    def as_bool(self, key):
948
1018
        """
949
1019
        Accepts a key as input. The corresponding value must be a string or
950
1020
        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
951
1021
        retain compatibility with Python 2.2.
952
 
 
953
 
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns
 
1022
        
 
1023
        If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
954
1024
        ``True``.
955
 
 
956
 
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns
 
1025
        
 
1026
        If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
957
1027
        ``False``.
958
 
 
 
1028
        
959
1029
        ``as_bool`` is not case sensitive.
960
 
 
 
1030
        
961
1031
        Any other input will raise a ``ValueError``.
962
 
 
 
1032
        
963
1033
        >>> a = ConfigObj()
964
1034
        >>> a['a'] = 'fish'
965
1035
        >>> a.as_bool('a')
979
1049
            return False
980
1050
        else:
981
1051
            try:
982
 
                if not isinstance(val, basestring):
 
1052
                if not isinstance(val, StringTypes):
983
1053
                    # TODO: Why do we raise a KeyError here?
984
1054
                    raise KeyError()
985
1055
                else:
991
1061
    def as_int(self, key):
992
1062
        """
993
1063
        A convenience method which coerces the specified value to an integer.
994
 
 
 
1064
        
995
1065
        If the value is an invalid literal for ``int``, a ``ValueError`` will
996
1066
        be raised.
997
 
 
 
1067
        
998
1068
        >>> a = ConfigObj()
999
1069
        >>> a['a'] = 'fish'
1000
1070
        >>> a.as_int('a')
1001
1071
        Traceback (most recent call last):
1002
 
        ValueError: invalid literal for int() with base 10: 'fish'
 
1072
        ValueError: invalid literal for int(): fish
1003
1073
        >>> a['b'] = '1'
1004
1074
        >>> a.as_int('b')
1005
1075
        1
1006
1076
        >>> a['b'] = '3.2'
1007
1077
        >>> a.as_int('b')
1008
1078
        Traceback (most recent call last):
1009
 
        ValueError: invalid literal for int() with base 10: '3.2'
 
1079
        ValueError: invalid literal for int(): 3.2
1010
1080
        """
1011
1081
        return int(self[key])
1012
1082
 
1014
1084
    def as_float(self, key):
1015
1085
        """
1016
1086
        A convenience method which coerces the specified value to a float.
1017
 
 
 
1087
        
1018
1088
        If the value is an invalid literal for ``float``, a ``ValueError`` will
1019
1089
        be raised.
1020
 
 
 
1090
        
1021
1091
        >>> a = ConfigObj()
1022
1092
        >>> a['a'] = 'fish'
1023
1093
        >>> a.as_float('a')
1033
1103
        return float(self[key])
1034
1104
 
1035
1105
 
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
1106
    def restore_default(self, key):
1059
1107
        """
1060
1108
        Restore (and return) default value for the specified key.
1061
 
 
 
1109
        
1062
1110
        This method will only work for a ConfigObj that was created
1063
1111
        with a configspec and has been validated.
1064
 
 
 
1112
        
1065
1113
        If there is no default value for this key, ``KeyError`` is raised.
1066
1114
        """
1067
1115
        default = self.default_values[key]
1070
1118
            self.defaults.append(key)
1071
1119
        return default
1072
1120
 
1073
 
 
 
1121
    
1074
1122
    def restore_defaults(self):
1075
1123
        """
1076
1124
        Recursively restore default values to all members
1077
1125
        that have them.
1078
 
 
 
1126
        
1079
1127
        This method will only work for a ConfigObj that was created
1080
1128
        with a configspec and has been validated.
1081
 
 
 
1129
        
1082
1130
        It doesn't delete or modify entries without default values.
1083
1131
        """
1084
1132
        for key in self.default_values:
1085
1133
            self.restore_default(key)
1086
 
 
 
1134
            
1087
1135
        for section in self.sections:
1088
1136
            self[section].restore_defaults()
1089
1137
 
1191
1239
        }
1192
1240
 
1193
1241
 
1194
 
    def __init__(self, infile=None, options=None, _inspec=False, **kwargs):
 
1242
    def __init__(self, infile=None, options=None, **kwargs):
1195
1243
        """
1196
1244
        Parse a config file or create a config file object.
1197
 
 
 
1245
        
1198
1246
        ``ConfigObj(infile=None, options=None, **kwargs)``
1199
1247
        """
1200
 
        self._inspec = _inspec
1201
1248
        # init the superclass
1202
1249
        Section.__init__(self, self, 0, self)
1203
 
 
1204
 
        infile = infile or []
1205
 
        options = dict(options or {})
1206
 
 
 
1250
        
 
1251
        if infile is None:
 
1252
            infile = []
 
1253
        if options is None:
 
1254
            options = {}
 
1255
        else:
 
1256
            options = dict(options)
 
1257
            
1207
1258
        # keyword arguments take precedence over an options dictionary
1208
1259
        options.update(kwargs)
1209
 
        if _inspec:
1210
 
            options['list_values'] = False
1211
 
 
 
1260
        
1212
1261
        defaults = OPTION_DEFAULTS.copy()
1213
1262
        # TODO: check the values too.
1214
1263
        for entry in options:
1215
1264
            if entry not in defaults:
1216
1265
                raise TypeError('Unrecognised option "%s".' % entry)
1217
 
 
 
1266
        
1218
1267
        # Add any explicit options to the defaults
1219
1268
        defaults.update(options)
1220
1269
        self._initialise(defaults)
1221
1270
        configspec = defaults['configspec']
1222
1271
        self._original_configspec = configspec
1223
1272
        self._load(infile, configspec)
1224
 
 
1225
 
 
 
1273
        
 
1274
        
1226
1275
    def _load(self, infile, configspec):
1227
 
        if isinstance(infile, basestring):
 
1276
        if isinstance(infile, StringTypes):
1228
1277
            self.filename = infile
1229
1278
            if os.path.isfile(infile):
1230
1279
                h = open(infile, 'rb')
1242
1291
                    h.write('')
1243
1292
                    h.close()
1244
1293
                infile = []
1245
 
 
 
1294
                
1246
1295
        elif isinstance(infile, (list, tuple)):
1247
1296
            infile = list(infile)
1248
 
 
 
1297
            
1249
1298
        elif isinstance(infile, dict):
1250
1299
            # initialise self
1251
1300
            # the Section class handles creating subsections
1252
1301
            if isinstance(infile, ConfigObj):
1253
1302
                # get a copy of our ConfigObj
1254
1303
                infile = infile.dict()
1255
 
 
 
1304
                
1256
1305
            for entry in infile:
1257
1306
                self[entry] = infile[entry]
1258
1307
            del self._errors
1259
 
 
 
1308
            
1260
1309
            if configspec is not None:
1261
1310
                self._handle_configspec(configspec)
1262
1311
            else:
1263
1312
                self.configspec = None
1264
1313
            return
1265
 
 
1266
 
        elif getattr(infile, 'read', MISSING) is not MISSING:
 
1314
        
 
1315
        elif getattr(infile, 'read', None) is not None:
1267
1316
            # This supports file like objects
1268
1317
            infile = infile.read() or []
1269
1318
            # needs splitting into lines - but needs doing *after* decoding
1270
1319
            # in case it's not an 8 bit encoding
1271
1320
        else:
1272
1321
            raise TypeError('infile must be a filename, file like object, or list of lines.')
1273
 
 
 
1322
        
1274
1323
        if infile:
1275
1324
            # don't do it for the empty ConfigObj
1276
1325
            infile = self._handle_bom(infile)
1288
1337
                break
1289
1338
 
1290
1339
            infile = [line.rstrip('\r\n') for line in infile]
1291
 
 
 
1340
            
1292
1341
        self._parse(infile)
1293
1342
        # if we had any errors, now is the time to raise them
1294
1343
        if self._errors:
1306
1355
            raise error
1307
1356
        # delete private attributes
1308
1357
        del self._errors
1309
 
 
 
1358
        
1310
1359
        if configspec is None:
1311
1360
            self.configspec = None
1312
1361
        else:
1313
1362
            self._handle_configspec(configspec)
1314
 
 
1315
 
 
 
1363
    
 
1364
    
1316
1365
    def _initialise(self, options=None):
1317
1366
        if options is None:
1318
1367
            options = OPTION_DEFAULTS
1319
 
 
 
1368
            
1320
1369
        # initialise a few variables
1321
1370
        self.filename = None
1322
1371
        self._errors = []
1333
1382
        self.newlines = None
1334
1383
        self.write_empty_values = options['write_empty_values']
1335
1384
        self.unrepr = options['unrepr']
1336
 
 
 
1385
        
1337
1386
        self.initial_comment = []
1338
1387
        self.final_comment = []
1339
 
        self.configspec = None
1340
 
 
1341
 
        if self._inspec:
1342
 
            self.list_values = False
1343
 
 
 
1388
        self.configspec = {}
 
1389
        
1344
1390
        # Clear section attributes as well
1345
1391
        Section._initialise(self)
1346
 
 
1347
 
 
 
1392
        
 
1393
        
1348
1394
    def __repr__(self):
1349
 
        return ('ConfigObj({%s})' %
1350
 
                ', '.join([('%s: %s' % (repr(key), repr(self[key])))
 
1395
        return ('ConfigObj({%s})' % 
 
1396
                ', '.join([('%s: %s' % (repr(key), repr(self[key]))) 
1351
1397
                for key in (self.scalars + self.sections)]))
1352
 
 
1353
 
 
 
1398
    
 
1399
    
1354
1400
    def _handle_bom(self, infile):
1355
1401
        """
1356
1402
        Handle any BOM, and decode if necessary.
1357
 
 
 
1403
        
1358
1404
        If an encoding is specified, that *must* be used - but the BOM should
1359
1405
        still be removed (and the BOM attribute set).
1360
 
 
 
1406
        
1361
1407
        (If the encoding is wrongly specified, then a BOM for an alternative
1362
1408
        encoding won't be discovered or removed.)
1363
 
 
 
1409
        
1364
1410
        If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1365
1411
        removed. The BOM attribute will be set. UTF16 will be decoded to
1366
1412
        unicode.
1367
 
 
 
1413
        
1368
1414
        NOTE: This method must not be called with an empty ``infile``.
1369
 
 
 
1415
        
1370
1416
        Specifying the *wrong* encoding is likely to cause a
1371
1417
        ``UnicodeDecodeError``.
1372
 
 
 
1418
        
1373
1419
        ``infile`` must always be returned as a list of lines, but may be
1374
1420
        passed in as a single string.
1375
1421
        """
1379
1425
            # the encoding specified doesn't have one
1380
1426
            # just decode
1381
1427
            return self._decode(infile, self.encoding)
1382
 
 
 
1428
        
1383
1429
        if isinstance(infile, (list, tuple)):
1384
1430
            line = infile[0]
1385
1431
        else:
1401
1447
                        ##self.BOM = True
1402
1448
                        # Don't need to remove BOM
1403
1449
                        return self._decode(infile, encoding)
1404
 
 
 
1450
                    
1405
1451
                # If we get this far, will *probably* raise a DecodeError
1406
1452
                # As it doesn't appear to start with a BOM
1407
1453
                return self._decode(infile, self.encoding)
1408
 
 
 
1454
            
1409
1455
            # Must be UTF8
1410
1456
            BOM = BOM_SET[enc]
1411
1457
            if not line.startswith(BOM):
1412
1458
                return self._decode(infile, self.encoding)
1413
 
 
 
1459
            
1414
1460
            newline = line[len(BOM):]
1415
 
 
 
1461
            
1416
1462
            # BOM removed
1417
1463
            if isinstance(infile, (list, tuple)):
1418
1464
                infile[0] = newline
1420
1466
                infile = newline
1421
1467
            self.BOM = True
1422
1468
            return self._decode(infile, self.encoding)
1423
 
 
 
1469
        
1424
1470
        # No encoding specified - so we need to check for UTF8/UTF16
1425
1471
        for BOM, (encoding, final_encoding) in BOMS.items():
1426
1472
            if not line.startswith(BOM):
1438
1484
                    else:
1439
1485
                        infile = newline
1440
1486
                    # UTF8 - don't decode
1441
 
                    if isinstance(infile, basestring):
 
1487
                    if isinstance(infile, StringTypes):
1442
1488
                        return infile.splitlines(True)
1443
1489
                    else:
1444
1490
                        return infile
1445
1491
                # UTF16 - have to decode
1446
1492
                return self._decode(infile, encoding)
1447
 
 
 
1493
            
1448
1494
        # No BOM discovered and no encoding specified, just return
1449
 
        if isinstance(infile, basestring):
 
1495
        if isinstance(infile, StringTypes):
1450
1496
            # infile read from a file will be a single string
1451
1497
            return infile.splitlines(True)
1452
1498
        return infile
1463
1509
    def _decode(self, infile, encoding):
1464
1510
        """
1465
1511
        Decode infile to unicode. Using the specified encoding.
1466
 
 
 
1512
        
1467
1513
        if is a string, it also needs converting to a list.
1468
1514
        """
1469
 
        if isinstance(infile, basestring):
 
1515
        if isinstance(infile, StringTypes):
1470
1516
            # can't be unicode
1471
1517
            # NOTE: Could raise a ``UnicodeDecodeError``
1472
1518
            return infile.decode(encoding).splitlines(True)
1493
1539
        Used by ``stringify`` within validate, to turn non-string values
1494
1540
        into strings.
1495
1541
        """
1496
 
        if not isinstance(value, basestring):
 
1542
        if not isinstance(value, StringTypes):
1497
1543
            return str(value)
1498
1544
        else:
1499
1545
            return value
1504
1550
        temp_list_values = self.list_values
1505
1551
        if self.unrepr:
1506
1552
            self.list_values = False
1507
 
 
 
1553
            
1508
1554
        comment_list = []
1509
1555
        done_start = False
1510
1556
        this_section = self
1511
1557
        maxline = len(infile) - 1
1512
1558
        cur_index = -1
1513
1559
        reset_comment = False
1514
 
 
 
1560
        
1515
1561
        while cur_index < maxline:
1516
1562
            if reset_comment:
1517
1563
                comment_list = []
1523
1569
                reset_comment = False
1524
1570
                comment_list.append(line)
1525
1571
                continue
1526
 
 
 
1572
            
1527
1573
            if not done_start:
1528
1574
                # preserve initial comment
1529
1575
                self.initial_comment = comment_list
1530
1576
                comment_list = []
1531
1577
                done_start = True
1532
 
 
 
1578
                
1533
1579
            reset_comment = True
1534
1580
            # first we check if it's a section marker
1535
1581
            mat = self._sectionmarker.match(line)
1543
1589
                    self._handle_error("Cannot compute the section depth at line %s.",
1544
1590
                                       NestingError, infile, cur_index)
1545
1591
                    continue
1546
 
 
 
1592
                
1547
1593
                if cur_depth < this_section.depth:
1548
1594
                    # the new section is dropping back to a previous level
1549
1595
                    try:
1562
1608
                else:
1563
1609
                    self._handle_error("Section too nested at line %s.",
1564
1610
                                       NestingError, infile, cur_index)
1565
 
 
 
1611
                    
1566
1612
                sect_name = self._unquote(sect_name)
1567
 
                if sect_name in parent:
 
1613
                if parent.has_key(sect_name):
1568
1614
                    self._handle_error('Duplicate section name at line %s.',
1569
1615
                                       DuplicateError, infile, cur_index)
1570
1616
                    continue
1571
 
 
 
1617
                
1572
1618
                # create the new section
1573
1619
                this_section = Section(
1574
1620
                    parent,
1642
1688
                            continue
1643
1689
                #
1644
1690
                key = self._unquote(key)
1645
 
                if key in this_section:
 
1691
                if this_section.has_key(key):
1646
1692
                    self._handle_error(
1647
1693
                        'Duplicate keyword name at line %s.',
1648
1694
                        DuplicateError, infile, cur_index)
1671
1717
        """
1672
1718
        Given a section and a depth level, walk back through the sections
1673
1719
        parents to see if the depth level matches a previous section.
1674
 
 
 
1720
        
1675
1721
        Return a reference to the right section,
1676
1722
        or raise a SyntaxError.
1677
1723
        """
1689
1735
    def _handle_error(self, text, ErrorClass, infile, cur_index):
1690
1736
        """
1691
1737
        Handle an error according to the error settings.
1692
 
 
 
1738
        
1693
1739
        Either raise the error or store it.
1694
1740
        The error will have occured at ``cur_index``
1695
1741
        """
1715
1761
    def _quote(self, value, multiline=True):
1716
1762
        """
1717
1763
        Return a safely quoted version of a value.
1718
 
 
 
1764
        
1719
1765
        Raise a ConfigObjError if the value cannot be safely quoted.
1720
1766
        If multiline is ``True`` (default) then use triple quotes
1721
1767
        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
 
 
 
1768
        
 
1769
        Don't quote values that don't need it.
 
1770
        Recursively quote members of a list and return a comma joined list.
 
1771
        Multiline is ``False`` for lists.
 
1772
        Obey list syntax for empty and single member lists.
 
1773
        
1728
1774
        If ``list_values=False`` then the value is only quoted if it contains
1729
 
        a ``\\n`` (is multiline) or '#'.
1730
 
 
 
1775
        a ``\n`` (is multiline) or '#'.
 
1776
        
1731
1777
        If ``write_empty_values`` is set, and the value is an empty string, it
1732
1778
        won't be quoted.
1733
1779
        """
1735
1781
            # Only if multiline is set, so that it is used for values not
1736
1782
            # keys, and not values that are part of a list
1737
1783
            return ''
1738
 
 
 
1784
        
1739
1785
        if multiline and isinstance(value, (list, tuple)):
1740
1786
            if not value:
1741
1787
                return ','
1743
1789
                return self._quote(value[0], multiline=False) + ','
1744
1790
            return ', '.join([self._quote(val, multiline=False)
1745
1791
                for val in value])
1746
 
        if not isinstance(value, basestring):
 
1792
        if not isinstance(value, StringTypes):
1747
1793
            if self.stringify:
1748
1794
                value = str(value)
1749
1795
            else:
1751
1797
 
1752
1798
        if not value:
1753
1799
            return '""'
1754
 
 
 
1800
        
1755
1801
        no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1756
1802
        need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1757
1803
        hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1758
1804
        check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1759
 
 
 
1805
        
1760
1806
        if check_for_single:
1761
1807
            if not self.list_values:
1762
1808
                # we don't quote if ``list_values=False``
1774
1820
        else:
1775
1821
            # if value has '\n' or "'" *and* '"', it will need triple quotes
1776
1822
            quot = self._get_triple_quote(value)
1777
 
 
 
1823
        
1778
1824
        if quot == noquot and '#' in value and self.list_values:
1779
1825
            quot = self._get_single_quote(value)
1780
 
 
 
1826
                
1781
1827
        return quot % value
1782
 
 
1783
 
 
 
1828
    
 
1829
    
1784
1830
    def _get_single_quote(self, value):
1785
1831
        if ("'" in value) and ('"' in value):
1786
1832
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1789
1835
        else:
1790
1836
            quot = dquot
1791
1837
        return quot
1792
 
 
1793
 
 
 
1838
    
 
1839
    
1794
1840
    def _get_triple_quote(self, value):
1795
1841
        if (value.find('"""') != -1) and (value.find("'''") != -1):
1796
1842
            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
1843
        if value.find('"""') == -1:
1800
 
            quot = tsquot
1801
 
        else:
1802
1844
            quot = tdquot
 
1845
        else:
 
1846
            quot = tsquot 
1803
1847
        return quot
1804
1848
 
1805
1849
 
1808
1852
        Given a value string, unquote, remove comment,
1809
1853
        handle lists. (including empty and single member lists)
1810
1854
        """
1811
 
        if self._inspec:
1812
 
            # Parsing a configspec so don't handle comments
1813
 
            return (value, '')
1814
1855
        # do we look for lists in values ?
1815
1856
        if not self.list_values:
1816
1857
            mat = self._nolistvalue.match(value)
1889
1930
 
1890
1931
    def _handle_configspec(self, configspec):
1891
1932
        """Parse the configspec."""
1892
 
        # FIXME: Should we check that the configspec was created with the
 
1933
        # FIXME: Should we check that the configspec was created with the 
1893
1934
        #        correct settings ? (i.e. ``list_values=False``)
1894
1935
        if not isinstance(configspec, ConfigObj):
1895
1936
            try:
1896
1937
                configspec = ConfigObj(configspec,
1897
1938
                                       raise_errors=True,
1898
1939
                                       file_error=True,
1899
 
                                       _inspec=True)
 
1940
                                       list_values=False)
1900
1941
            except ConfigObjError, e:
1901
1942
                # FIXME: Should these errors have a reference
1902
1943
                #        to the already parsed ConfigObj ?
1903
1944
                raise ConfigspecError('Parsing configspec failed: %s' % e)
1904
1945
            except IOError, e:
1905
1946
                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
 
 
 
1947
        
 
1948
        self._set_configspec_value(configspec, self)
 
1949
 
 
1950
 
 
1951
    def _set_configspec_value(self, configspec, section):
 
1952
        """Used to recursively set configspec values."""
 
1953
        if '__many__' in configspec.sections:
 
1954
            section.configspec['__many__'] = configspec['__many__']
 
1955
            if len(configspec.sections) > 1:
 
1956
                # FIXME: can we supply any useful information here ?
 
1957
                raise RepeatSectionError()
 
1958
            
 
1959
        if getattr(configspec, 'initial_comment', None) is not None:
 
1960
            section._configspec_initial_comment = configspec.initial_comment
 
1961
            section._configspec_final_comment = configspec.final_comment
 
1962
            section._configspec_encoding = configspec.encoding
 
1963
            section._configspec_BOM = configspec.BOM
 
1964
            section._configspec_newlines = configspec.newlines
 
1965
            section._configspec_indent_type = configspec.indent_type
 
1966
            
 
1967
        for entry in configspec.scalars:
 
1968
            section._configspec_comments[entry] = configspec.comments[entry]
 
1969
            section._configspec_inline_comments[entry] = configspec.inline_comments[entry]
 
1970
            section.configspec[entry] = configspec[entry]
 
1971
            section._order.append(entry)
 
1972
            
1923
1973
        for entry in configspec.sections:
1924
1974
            if entry == '__many__':
1925
1975
                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]
 
1976
            
 
1977
            section._cs_section_comments[entry] = configspec.comments[entry]
 
1978
            section._cs_section_inline_comments[entry] = configspec.inline_comments[entry]
 
1979
            if not section.has_key(entry):
 
1980
                section[entry] = {}
 
1981
            self._set_configspec_value(configspec[entry], section[entry])
 
1982
 
 
1983
 
 
1984
    def _handle_repeat(self, section, configspec):
 
1985
        """Dynamically assign configspec for repeated section."""
 
1986
        try:
 
1987
            section_keys = configspec.sections
 
1988
            scalar_keys = configspec.scalars
 
1989
        except AttributeError:
 
1990
            section_keys = [entry for entry in configspec 
 
1991
                                if isinstance(configspec[entry], dict)]
 
1992
            scalar_keys = [entry for entry in configspec 
 
1993
                                if not isinstance(configspec[entry], dict)]
 
1994
            
 
1995
        if '__many__' in section_keys and len(section_keys) > 1:
 
1996
            # FIXME: can we supply any useful information here ?
 
1997
            raise RepeatSectionError()
 
1998
        
 
1999
        scalars = {}
 
2000
        sections = {}
 
2001
        for entry in scalar_keys:
 
2002
            val = configspec[entry]
 
2003
            scalars[entry] = val
 
2004
        for entry in section_keys:
 
2005
            val = configspec[entry]
 
2006
            if entry == '__many__':
 
2007
                scalars[entry] = val
 
2008
                continue
 
2009
            sections[entry] = val
 
2010
            
 
2011
        section.configspec = scalars
 
2012
        for entry in sections:
 
2013
            if not section.has_key(entry):
 
2014
                section[entry] = {}
 
2015
            self._handle_repeat(section[entry], sections[entry])
1936
2016
 
1937
2017
 
1938
2018
    def _write_line(self, indent_string, entry, this_entry, comment):
1973
2053
    def write(self, outfile=None, section=None):
1974
2054
        """
1975
2055
        Write the current ConfigObj as a file
1976
 
 
 
2056
        
1977
2057
        tekNico: FIXME: use StringIO instead of real files
1978
 
 
 
2058
        
1979
2059
        >>> filename = a.filename
1980
2060
        >>> a.filename = 'test.ini'
1981
2061
        >>> a.write()
1986
2066
        if self.indent_type is None:
1987
2067
            # this can be true if initialised from a dictionary
1988
2068
            self.indent_type = DEFAULT_INDENT_TYPE
1989
 
 
 
2069
            
1990
2070
        out = []
1991
2071
        cs = self._a_to_u('#')
1992
2072
        csp = self._a_to_u('# ')
2000
2080
                if stripped_line and not stripped_line.startswith(cs):
2001
2081
                    line = csp + line
2002
2082
                out.append(line)
2003
 
 
 
2083
                
2004
2084
        indent_string = self.indent_type * section.depth
2005
2085
        for entry in (section.scalars + section.sections):
2006
2086
            if entry in section.defaults:
2013
2093
                out.append(indent_string + comment_line)
2014
2094
            this_entry = section[entry]
2015
2095
            comment = self._handle_comment(section.inline_comments[entry])
2016
 
 
 
2096
            
2017
2097
            if isinstance(this_entry, dict):
2018
2098
                # a section
2019
2099
                out.append(self._write_marker(
2028
2108
                    entry,
2029
2109
                    this_entry,
2030
2110
                    comment))
2031
 
 
 
2111
                
2032
2112
        if section is self:
2033
2113
            for line in self.final_comment:
2034
2114
                line = self._decode_element(line)
2037
2117
                    line = csp + line
2038
2118
                out.append(line)
2039
2119
            self.interpolation = int_val
2040
 
 
 
2120
            
2041
2121
        if section is not self:
2042
2122
            return out
2043
 
 
 
2123
        
2044
2124
        if (self.filename is None) and (outfile is None):
2045
2125
            # output a list of lines
2046
2126
            # might need to encode
2054
2134
                    out.append('')
2055
2135
                out[0] = BOM_UTF8 + out[0]
2056
2136
            return out
2057
 
 
 
2137
        
2058
2138
        # Turn the list to a string, joined with correct newlines
2059
2139
        newline = self.newlines or os.linesep
2060
2140
        output = self._a_to_u(newline).join(out)
2063
2143
        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2064
2144
            # Add the UTF8 BOM
2065
2145
            output = BOM_UTF8 + output
2066
 
 
 
2146
            
2067
2147
        if not output.endswith(newline):
2068
2148
            output += newline
2069
2149
        if outfile is not None:
2078
2158
                 section=None):
2079
2159
        """
2080
2160
        Test the ConfigObj against a configspec.
2081
 
 
 
2161
        
2082
2162
        It uses the ``validator`` object from *validate.py*.
2083
 
 
 
2163
        
2084
2164
        To run ``validate`` on the current ConfigObj, call: ::
2085
 
 
 
2165
        
2086
2166
            test = config.validate(validator)
2087
 
 
 
2167
        
2088
2168
        (Normally having previously passed in the configspec when the ConfigObj
2089
2169
        was created - you can dynamically assign a dictionary of checks to the
2090
2170
        ``configspec`` attribute of a section though).
2091
 
 
 
2171
        
2092
2172
        It returns ``True`` if everything passes, or a dictionary of
2093
2173
        pass/fails (True/False). If every member of a subsection passes, it
2094
2174
        will just have the value ``True``. (It also returns ``False`` if all
2095
2175
        members fail).
2096
 
 
 
2176
        
2097
2177
        In addition, it converts the values from strings to their native
2098
2178
        types if their checks pass (and ``stringify`` is set).
2099
 
 
 
2179
        
2100
2180
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2101
2181
        of a marking a fail with a ``False``, it will preserve the actual
2102
2182
        exception object. This can contain info about the reason for failure.
2103
2183
        For example the ``VdtValueTooSmallError`` indicates that the value
2104
2184
        supplied was too small. If a value (or section) is missing it will
2105
2185
        still be marked as ``False``.
2106
 
 
 
2186
        
2107
2187
        You must have the validate module to use ``preserve_errors=True``.
2108
 
 
 
2188
        
2109
2189
        You can then use the ``flatten_errors`` function to turn your nested
2110
2190
        results dictionary into a flattened list of failures - useful for
2111
2191
        displaying meaningful error messages.
2118
2198
                # Which makes importing configobj faster
2119
2199
                from validate import VdtMissingValue
2120
2200
                self._vdtMissingValue = VdtMissingValue
2121
 
 
2122
2201
            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):
 
2202
        #
 
2203
        spec_section = section.configspec
 
2204
        if copy and getattr(section, '_configspec_initial_comment', None) is not None:
 
2205
            section.initial_comment = section._configspec_initial_comment
 
2206
            section.final_comment = section._configspec_final_comment
 
2207
            section.encoding = section._configspec_encoding
 
2208
            section.BOM = section._configspec_BOM
 
2209
            section.newlines = section._configspec_newlines
 
2210
            section.indent_type = section._configspec_indent_type
 
2211
            
 
2212
        if '__many__' in section.configspec:
 
2213
            many = spec_section['__many__']
 
2214
            # dynamically assign the configspecs
 
2215
            # for the sections below
 
2216
            for entry in section.sections:
 
2217
                self._handle_repeat(section[entry], many)
 
2218
        #
 
2219
        out = {}
 
2220
        ret_true = True
 
2221
        ret_false = True
 
2222
        order = [k for k in section._order if k in spec_section]
 
2223
        order += [k for k in spec_section if k not in order]
 
2224
        for entry in order:
 
2225
            if entry == '__many__':
 
2226
                continue
 
2227
            if (not entry in section.scalars) or (entry in section.defaults):
 
2228
                # missing entries
 
2229
                # or entries from defaults
 
2230
                missing = True
 
2231
                val = None
 
2232
                if copy and not entry in section.scalars:
 
2233
                    # copy comments
 
2234
                    section.comments[entry] = (
 
2235
                        section._configspec_comments.get(entry, []))
 
2236
                    section.inline_comments[entry] = (
 
2237
                        section._configspec_inline_comments.get(entry, ''))
 
2238
                #
 
2239
            else:
 
2240
                missing = False
 
2241
                val = section[entry]
2137
2242
            try:
2138
 
                check = validator.check(spec,
 
2243
                check = validator.check(spec_section[entry],
2139
2244
                                        val,
2140
2245
                                        missing=missing
2141
2246
                                        )
2148
2253
                    ret_false = False
2149
2254
                ret_true = False
2150
2255
            else:
2151
 
                try:
 
2256
                try: 
2152
2257
                    section.default_values.pop(entry, None)
2153
 
                except AttributeError:
 
2258
                except AttributeError: 
2154
2259
                    # For Python 2.2 compatibility
2155
2260
                    try:
2156
2261
                        del section.default_values[entry]
2157
2262
                    except KeyError:
2158
2263
                        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
 
 
 
2264
                    
 
2265
                if getattr(validator, 'get_default_value', None) is not None:
 
2266
                    try: 
 
2267
                        section.default_values[entry] = validator.get_default_value(spec_section[entry])
 
2268
                    except KeyError:
 
2269
                        # No default
 
2270
                        pass
 
2271
                    
2166
2272
                ret_false = False
2167
2273
                out[entry] = True
2168
2274
                if self.stringify or missing:
2181
2287
                        section[entry] = check
2182
2288
                if not copy and missing and entry not in section.defaults:
2183
2289
                    section.defaults.append(entry)
2184
 
            return ret_true, ret_false
2185
 
 
2186
 
        #
2187
 
        out = {}
2188
 
        ret_true = True
2189
 
        ret_false = True
2190
 
 
2191
 
        unvalidated = [k for k in section.scalars if k not in configspec]
2192
 
        incorrect_sections = [k for k in configspec.sections if k in section.scalars]
2193
 
        incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2194
 
 
2195
 
        for entry in configspec.scalars:
2196
 
            if entry in ('__many__', '___many___'):
2197
 
                # reserved names
2198
 
                continue
2199
 
 
2200
 
            if (not entry in section.scalars) or (entry in section.defaults):
2201
 
                # missing entries
2202
 
                # or entries from defaults
2203
 
                missing = True
2204
 
                val = None
2205
 
                if copy and not entry in section.scalars:
2206
 
                    # copy comments
2207
 
                    section.comments[entry] = (
2208
 
                        configspec.comments.get(entry, []))
2209
 
                    section.inline_comments[entry] = (
2210
 
                        configspec.inline_comments.get(entry, ''))
2211
 
                #
2212
 
            else:
2213
 
                missing = False
2214
 
                val = section[entry]
2215
 
 
2216
 
            ret_true, ret_false = validate_entry(entry, configspec[entry], val,
2217
 
                                                 missing, ret_true, ret_false)
2218
 
 
2219
 
        many = None
2220
 
        if '__many__' in configspec.scalars:
2221
 
            many = configspec['__many__']
2222
 
        elif '___many___' in configspec.scalars:
2223
 
            many = configspec['___many___']
2224
 
 
2225
 
        if many is not None:
2226
 
            for entry in unvalidated:
2227
 
                val = section[entry]
2228
 
                ret_true, ret_false = validate_entry(entry, many, val, False,
2229
 
                                                     ret_true, ret_false)
2230
 
 
2231
 
        for entry in incorrect_scalars:
2232
 
            ret_true = False
2233
 
            if not preserve_errors:
2234
 
                out[entry] = False
2235
 
            else:
2236
 
                ret_false = False
2237
 
                msg = 'Value %r was provided as a section' % entry
2238
 
                out[entry] = validator.baseErrorClass(msg)
2239
 
        for entry in incorrect_sections:
2240
 
            ret_true = False
2241
 
            if not preserve_errors:
2242
 
                out[entry] = False
2243
 
            else:
2244
 
                ret_false = False
2245
 
                msg = 'Section %r was provided as a single value' % entry
2246
 
                out[entry] = validator.baseErrorClass(msg)
2247
 
 
2248
2290
        # Missing sections will have been created as empty ones when the
2249
2291
        # configspec was read.
2250
2292
        for entry in section.sections:
2251
2293
            # FIXME: this means DEFAULT is not copied in copy mode
2252
2294
            if section is self and entry == 'DEFAULT':
2253
2295
                continue
2254
 
            if section[entry].configspec is None:
2255
 
                continue
2256
2296
            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])
 
2297
                section.comments[entry] = section._cs_section_comments[entry]
 
2298
                section.inline_comments[entry] = (
 
2299
                    section._cs_section_inline_comments[entry])
 
2300
            check = self.validate(validator, preserve_errors=preserve_errors,
 
2301
                copy=copy, section=section[entry])
2260
2302
            out[entry] = check
2261
2303
            if check == False:
2262
2304
                ret_true = False
2282
2324
        self.configspec = None
2283
2325
        # Just to be sure ;-)
2284
2326
        self._original_configspec = None
2285
 
 
2286
 
 
 
2327
        
 
2328
        
2287
2329
    def reload(self):
2288
2330
        """
2289
2331
        Reload a ConfigObj from file.
2290
 
 
 
2332
        
2291
2333
        This method raises a ``ReloadError`` if the ConfigObj doesn't have
2292
2334
        a filename attribute pointing to a file.
2293
2335
        """
2294
 
        if not isinstance(self.filename, basestring):
 
2336
        if not isinstance(self.filename, StringTypes):
2295
2337
            raise ReloadError()
2296
2338
 
2297
2339
        filename = self.filename
2300
2342
            if entry == 'configspec':
2301
2343
                continue
2302
2344
            current_options[entry] = getattr(self, entry)
2303
 
 
 
2345
            
2304
2346
        configspec = self._original_configspec
2305
2347
        current_options['configspec'] = configspec
2306
 
 
 
2348
            
2307
2349
        self.clear()
2308
2350
        self._initialise(current_options)
2309
2351
        self._load(filename, configspec)
2310
 
 
 
2352
        
2311
2353
 
2312
2354
 
2313
2355
class SimpleVal(object):
2314
2356
    """
2315
2357
    A simple validator.
2316
2358
    Can be used to check that all members expected are present.
2317
 
 
 
2359
    
2318
2360
    To use it, provide a configspec with all your members in (the value given
2319
2361
    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2320
2362
    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2321
2363
    members are present, or a dictionary with True/False meaning
2322
2364
    present/missing. (Whole missing sections will be replaced with ``False``)
2323
2365
    """
2324
 
 
 
2366
    
2325
2367
    def __init__(self):
2326
2368
        self.baseErrorClass = ConfigObjError
2327
 
 
 
2369
    
2328
2370
    def check(self, check, member, missing=False):
2329
2371
        """A dummy check method, always returns the value unchanged."""
2330
2372
        if missing:
2337
2379
    """
2338
2380
    An example function that will turn a nested dictionary of results
2339
2381
    (as returned by ``ConfigObj.validate``) into a flat list.
2340
 
 
 
2382
    
2341
2383
    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2342
2384
    dictionary returned by ``validate``.
2343
 
 
 
2385
    
2344
2386
    (This is a recursive function, so you shouldn't use the ``levels`` or
2345
 
    ``results`` arguments - they are used by the function.)
2346
 
 
 
2387
    ``results`` arguments - they are used by the function.
 
2388
    
2347
2389
    Returns a list of keys that failed. Each member of the list is a tuple :
2348
 
 
2349
2390
    ::
2350
 
 
 
2391
    
2351
2392
        ([list of sections...], key, result)
2352
 
 
 
2393
    
2353
2394
    If ``validate`` was called with ``preserve_errors=False`` (the default)
2354
2395
    then ``result`` will always be ``False``.
2355
2396
 
2356
2397
    *list of sections* is a flattened list of sections that the key was found
2357
2398
    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
 
 
 
2399
    
 
2400
    If the section was missing then key will be ``None``.
 
2401
    
2362
2402
    If the value (or section) was missing then ``result`` will be ``False``.
2363
 
 
 
2403
    
2364
2404
    If ``validate`` was called with ``preserve_errors=True`` and a value
2365
2405
    was present, but failed the check, then ``result`` will be the exception
2366
2406
    object returned. You can use this as a string that describes the failure.
2367
 
 
 
2407
    
2368
2408
    For example *The value "3" is of the wrong type*.
2369
 
 
 
2409
    
2370
2410
    >>> import validate
2371
2411
    >>> vtor = validate.Validator()
2372
2412
    >>> my_ini = '''
2436
2476
        results = []
2437
2477
    if res is True:
2438
2478
        return results
2439
 
    if res is False or isinstance(res, Exception):
2440
 
        results.append((levels[:], None, res))
 
2479
    if res is False:
 
2480
        results.append((levels[:], None, False))
2441
2481
        if levels:
2442
2482
            levels.pop()
2443
2483
        return results