~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Robert Collins
  • Date: 2010-06-25 20:34:05 UTC
  • mto: This revision was merged to the branch mainline in revision 5324.
  • Revision ID: robertc@robertcollins.net-20100625203405-c74lxd3enklhaqf9
``bzrlib.osutils.get_terminal_encoding`` will now only mutter its
selection when explicitly requested; this avoids many duplicate calls
being logged when helpers, wrappers and older code that manually calls
it are executed it is now logged deliberately by the ui setup code.
(Robert Collins)

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