~abentley/bzr/nested-trees

« back to all changes in this revision

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

  • Committer: Aaron Bentley
  • Date: 2009-04-30 14:00:40 UTC
  • mfrom: (1907.1.31 merge)
  • Revision ID: aaron@aaronbentley.com-20090430140040-ataetqn5pyl4t376
Merge merge into current

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
 
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
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
    """
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
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
 
598
601
        Values need only be strings (or lists of strings) if
599
602
        ``main.stringify`` is set.
600
603
 
601
 
        `unrepr`` must be set when setting a value to a dictionary, without
 
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
 
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):
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
879
882
 
880
883
        See  the encode and decode methods for examples, including functions.
881
884
 
882
 
        .. caution::
 
885
        .. admonition:: caution
883
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.
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
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:
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
 
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.
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
1203
 
1255
 
        if infile is None:
1256
 
            infile = []
1257
 
        if options is None:
1258
 
            options = {}
1259
 
        else:
1260
 
            options = dict(options)
 
1204
        infile = infile or []
 
1205
        options = dict(options or {})
1261
1206
 
1262
1207
        # keyword arguments take precedence over an options dictionary
1263
1208
        options.update(kwargs)
 
1209
        if _inspec:
 
1210
            options['list_values'] = False
1264
1211
 
1265
1212
        defaults = OPTION_DEFAULTS.copy()
1266
1213
        # TODO: check the values too.
1277
1224
 
1278
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')
1316
1263
                self.configspec = None
1317
1264
            return
1318
1265
 
1319
 
        elif getattr(infile, 'read', None) is not None:
 
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
1389
1336
 
1390
1337
        self.initial_comment = []
1391
1338
        self.final_comment = []
1392
 
        self.configspec = {}
 
1339
        self.configspec = None
 
1340
 
 
1341
        if self._inspec:
 
1342
            self.list_values = False
1393
1343
 
1394
1344
        # Clear section attributes as well
1395
1345
        Section._initialise(self)
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
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
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
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
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)
1770
1720
        If multiline is ``True`` (default) then use triple quotes
1771
1721
        if necessary.
1772
1722
 
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.
 
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.
1777
1727
 
1778
1728
        If ``list_values=False`` then the value is only quoted if it contains
1779
 
        a ``\n`` (is multiline) or '#'.
 
1729
        a ``\\n`` (is multiline) or '#'.
1780
1730
 
1781
1731
        If ``write_empty_values`` is set, and the value is an empty string, it
1782
1732
        won't be quoted.
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:
1856
1806
        Given a value string, unquote, remove comment,
1857
1807
        handle lists. (including empty and single member lists)
1858
1808
        """
 
1809
        if self._inspec:
 
1810
            # Parsing a configspec so don't handle comments
 
1811
            return (value, '')
1859
1812
        # do we look for lists in values ?
1860
1813
        if not self.list_values:
1861
1814
            mat = self._nolistvalue.match(value)
1941
1894
                configspec = ConfigObj(configspec,
1942
1895
                                       raise_errors=True,
1943
1896
                                       file_error=True,
1944
 
                                       list_values=False)
 
1897
                                       _inspec=True)
1945
1898
            except ConfigObjError, e:
1946
1899
                # FIXME: Should these errors have a reference
1947
1900
                #        to the already parsed ConfigObj ?
1949
1902
            except IOError, e:
1950
1903
                raise IOError('Reading configspec failed: %s' % e)
1951
1904
 
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)
 
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
1976
1920
 
1977
1921
        for entry in configspec.sections:
1978
1922
            if entry == '__many__':
1979
1923
                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])
 
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]
2020
1934
 
2021
1935
 
2022
1936
    def _write_line(self, indent_string, entry, this_entry, comment):
2202
2116
                # Which makes importing configobj faster
2203
2117
                from validate import VdtMissingValue
2204
2118
                self._vdtMissingValue = VdtMissingValue
 
2119
 
2205
2120
            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]
 
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):
2246
2135
            try:
2247
 
                check = validator.check(spec_section[entry],
 
2136
                check = validator.check(spec,
2248
2137
                                        val,
2249
2138
                                        missing=missing
2250
2139
                                        )
2266
2155
                    except KeyError:
2267
2156
                        pass
2268
2157
 
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
 
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
2275
2163
 
2276
2164
                ret_false = False
2277
2165
                out[entry] = True
2291
2179
                        section[entry] = check
2292
2180
                if not copy and missing and entry not in section.defaults:
2293
2181
                    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
 
2294
2246
        # Missing sections will have been created as empty ones when the
2295
2247
        # configspec was read.
2296
2248
        for entry in section.sections:
2297
2249
            # FIXME: this means DEFAULT is not copied in copy mode
2298
2250
            if section is self and entry == 'DEFAULT':
2299
2251
                continue
 
2252
            if section[entry].configspec is None:
 
2253
                continue
2300
2254
            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])
 
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])
2306
2258
            out[entry] = check
2307
2259
            if check == False:
2308
2260
                ret_true = False
2337
2289
        This method raises a ``ReloadError`` if the ConfigObj doesn't have
2338
2290
        a filename attribute pointing to a file.
2339
2291
        """
2340
 
        if not isinstance(self.filename, StringTypes):
 
2292
        if not isinstance(self.filename, basestring):
2341
2293
            raise ReloadError()
2342
2294
 
2343
2295
        filename = self.filename
2388
2340
    dictionary returned by ``validate``.
2389
2341
 
2390
2342
    (This is a recursive function, so you shouldn't use the ``levels`` or
2391
 
    ``results`` arguments - they are used by the function.
 
2343
    ``results`` arguments - they are used by the function.)
2392
2344
 
2393
2345
    Returns a list of keys that failed. Each member of the list is a tuple :
 
2346
 
2394
2347
    ::
2395
2348
 
2396
2349
        ([list of sections...], key, result)
2401
2354
    *list of sections* is a flattened list of sections that the key was found
2402
2355
    in.
2403
2356
 
2404
 
    If the section was missing then key will be ``None``.
 
2357
    If the section was missing (or a section was expected and a scalar provided
 
2358
    - or vice-versa) then key will be ``None``.
2405
2359
 
2406
2360
    If the value (or section) was missing then ``result`` will be ``False``.
2407
2361
 
2480
2434
        results = []
2481
2435
    if res is True:
2482
2436
        return results
2483
 
    if res is False:
2484
 
        results.append((levels[:], None, False))
 
2437
    if res is False or isinstance(res, Exception):
 
2438
        results.append((levels[:], None, res))
2485
2439
        if levels:
2486
2440
            levels.pop()
2487
2441
        return results