~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Martin Pool
  • Date: 2009-01-26 17:04:21 UTC
  • mfrom: (3932.2.7 prepare-1.11)
  • mto: This revision was merged to the branch mainline in revision 3960.
  • Revision ID: mbp@sourcefrog.net-20090126170421-dypt01en9zx8y16o
MergeĀ backĀ 1.11final

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