~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: 2011-08-17 18:13:57 UTC
  • mfrom: (5268.7.29 transport-segments)
  • Revision ID: pqm@pqm.ubuntu.com-20110817181357-y5q5eth1hk8bl3om
(jelmer) Allow specifying the colocated branch to use in the branch URL,
 and retrieving the branch name using ControlDir._get_selected_branch.
 (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

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