/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

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

  • Committer: Matt Nordhoff
  • Date: 2009-04-17 22:24:54 UTC
  • mto: This revision was merged to the branch mainline in revision 4314.
  • Revision ID: mnordhoff@mattnordhoff.com-20090417222454-frskxbd4w812ecnj
Restore Bazaar's customizations to configobj.py:

* Strip trailing whitespace

* Replace "has_key" with "in"

* Don't import the compiler module; it's slow and we don't need it

Other customizations Bazaar previously made have been merged upstream:

* Replace hasattr with getattr

* Don't use the deprecated "message" attribute on exceptions

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
import re
25
25
 
26
26
compiler = None
27
 
try:
28
 
    import compiler
29
 
except ImportError:
30
 
    # for IronPython
31
 
    pass
 
27
# Bzr modification: Disabled import of 'compiler' module
 
28
# bzr doesn't use the 'unrepr' feature of configobj, so importing compiler just
 
29
# wastes several milliseconds on every single bzr invocation.
 
30
#   -- Andrew Bennetts, 2008-10-14
 
31
#try:
 
32
#    import compiler
 
33
#except ImportError:
 
34
#    # for IronPython
 
35
#    pass
32
36
 
33
37
 
34
38
try:
117
121
 
118
122
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
119
123
 
120
 
__docformat__ = "restructuredtext en" 
 
124
__docformat__ = "restructuredtext en"
121
125
 
122
126
__all__ = (
123
127
    '__version__',
176
180
 
177
181
 
178
182
class Builder(object):
179
 
    
 
183
 
180
184
    def build(self, o):
181
185
        m = getattr(self, 'build_' + o.__class__.__name__, None)
182
186
        if m is None:
183
187
            raise UnknownType(o.__class__.__name__)
184
188
        return m(o)
185
 
    
 
189
 
186
190
    def build_List(self, o):
187
191
        return map(self.build, o.getChildren())
188
 
    
 
192
 
189
193
    def build_Const(self, o):
190
194
        return o.value
191
 
    
 
195
 
192
196
    def build_Dict(self, o):
193
197
        d = {}
194
198
        i = iter(map(self.build, o.getChildren()))
195
199
        for el in i:
196
200
            d[el] = i.next()
197
201
        return d
198
 
    
 
202
 
199
203
    def build_Tuple(self, o):
200
204
        return tuple(self.build_List(o))
201
 
    
 
205
 
202
206
    def build_Name(self, o):
203
207
        if o.name == 'None':
204
208
            return None
206
210
            return True
207
211
        if o.name == 'False':
208
212
            return False
209
 
        
 
213
 
210
214
        # An undefined Name
211
215
        raise UnknownType('Undefined Name')
212
 
    
 
216
 
213
217
    def build_Add(self, o):
214
218
        real, imag = map(self.build_Const, o.getChildren())
215
219
        try:
219
223
        if not isinstance(imag, complex) or imag.real != 0.0:
220
224
            raise UnknownType('Add')
221
225
        return real+imag
222
 
    
 
226
 
223
227
    def build_Getattr(self, o):
224
228
        parent = self.build(o.expr)
225
229
        return getattr(parent, o.attrname)
226
 
    
 
230
 
227
231
    def build_UnarySub(self, o):
228
232
        return -self.build_Const(o.getChildren()[0])
229
 
    
 
233
 
230
234
    def build_UnaryAdd(self, o):
231
235
        return self.build_Const(o.getChildren()[0])
232
236
 
349
353
            This is similar to a depth-first-search algorithm.
350
354
            """
351
355
            # Have we been here already?
352
 
            if backtrail.has_key((key, section.name)):
 
356
            if (key, section.name) in backtrail:
353
357
                # Yes - infinite loop detected
354
358
                raise InterpolationLoopError(key)
355
359
            # Place a marker on our backtrail so we won't come back here again
437
441
        (e.g., if we interpolated "$$" and returned "$").
438
442
        """
439
443
        raise NotImplementedError()
440
 
    
 
444
 
441
445
 
442
446
 
443
447
class ConfigParserInterpolation(InterpolationEngine):
484
488
 
485
489
def __newobj__(cls, *args):
486
490
    # Hack for pickle
487
 
    return cls.__new__(cls, *args) 
 
491
    return cls.__new__(cls, *args)
488
492
 
489
493
class Section(dict):
490
494
    """
491
495
    A dictionary-like object that represents a section in a config file.
492
 
    
 
496
 
493
497
    It does string interpolation if the 'interpolation' attribute
494
498
    of the 'main' object is set to True.
495
 
    
 
499
 
496
500
    Interpolation is tried first from this object, then from the 'DEFAULT'
497
501
    section of this object, next from the parent and its 'DEFAULT' section,
498
502
    and so on until the main object is reached.
499
 
    
 
503
 
500
504
    A Section will behave like an ordered dictionary - following the
501
505
    order of the ``scalars`` and ``sections`` attributes.
502
506
    You can use this to change the order of members.
503
 
    
 
507
 
504
508
    Iteration follows the order: scalars, then sections.
505
509
    """
506
510
 
507
 
    
 
511
 
508
512
    def __setstate__(self, state):
509
513
        dict.update(self, state[0])
510
514
        self.__dict__.update(state[1])
512
516
    def __reduce__(self):
513
517
        state = (dict(self), self.__dict__)
514
518
        return (__newobj__, (self.__class__,), state)
515
 
    
516
 
    
 
519
 
 
520
 
517
521
    def __init__(self, parent, depth, main, indict=None, name=None):
518
522
        """
519
523
        * parent is the section above
538
542
        # (rather than just passing to ``dict.__init__``)
539
543
        for entry, value in indict.iteritems():
540
544
            self[entry] = value
541
 
            
542
 
            
 
545
 
 
546
 
543
547
    def _initialise(self):
544
548
        # the sequence of scalar values in this Section
545
549
        self.scalars = []
589
593
    def __setitem__(self, key, value, unrepr=False):
590
594
        """
591
595
        Correctly set a value.
592
 
        
 
596
 
593
597
        Making dictionary values Section instances.
594
598
        (We have to special case 'Section' instances - which are also dicts)
595
 
        
 
599
 
596
600
        Keys must be strings.
597
601
        Values need only be strings (or lists of strings) if
598
602
        ``main.stringify`` is set.
599
 
        
 
603
 
600
604
        ``unrepr`` must be set when setting a value to a dictionary, without
601
605
        creating a new sub-section.
602
606
        """
603
607
        if not isinstance(key, basestring):
604
608
            raise ValueError('The key "%s" is not a string.' % key)
605
 
        
 
609
 
606
610
        # add the comment
607
 
        if not self.comments.has_key(key):
 
611
        if key not in self.comments:
608
612
            self.comments[key] = []
609
613
            self.inline_comments[key] = ''
610
614
        # remove the entry from defaults
612
616
            self.defaults.remove(key)
613
617
        #
614
618
        if isinstance(value, Section):
615
 
            if not self.has_key(key):
 
619
            if key not in self:
616
620
                self.sections.append(key)
617
621
            dict.__setitem__(self, key, value)
618
622
        elif isinstance(value, dict) and not unrepr:
619
623
            # First create the new depth level,
620
624
            # then create the section
621
 
            if not self.has_key(key):
 
625
            if key not in self:
622
626
                self.sections.append(key)
623
627
            new_depth = self.depth + 1
624
628
            dict.__setitem__(
631
635
                    indict=value,
632
636
                    name=key))
633
637
        else:
634
 
            if not self.has_key(key):
 
638
            if key not in self:
635
639
                self.scalars.append(key)
636
640
            if not self.main.stringify:
637
641
                if isinstance(value, basestring):
706
710
        """
707
711
        A version of clear that also affects scalars/sections
708
712
        Also clears comments and configspec.
709
 
        
 
713
 
710
714
        Leaves other attributes alone :
711
715
            depth/main/parent are not affected
712
716
        """
773
777
    def dict(self):
774
778
        """
775
779
        Return a deepcopy of self as a dictionary.
776
 
        
 
780
 
777
781
        All members that are ``Section`` instances are recursively turned to
778
782
        ordinary dictionaries - by calling their ``dict`` method.
779
 
        
 
783
 
780
784
        >>> n = a.dict()
781
785
        >>> n == a
782
786
        1
801
805
    def merge(self, indict):
802
806
        """
803
807
        A recursive update - useful for merging config files.
804
 
        
 
808
 
805
809
        >>> a = '''[section1]
806
810
        ...     option1 = True
807
811
        ...     [[subsection]]
821
825
            if (key in self and isinstance(self[key], dict) and
822
826
                                isinstance(val, dict)):
823
827
                self[key].merge(val)
824
 
            else:   
 
828
            else:
825
829
                self[key] = val
826
830
 
827
831
 
828
832
    def rename(self, oldkey, newkey):
829
833
        """
830
834
        Change a keyname to another, without changing position in sequence.
831
 
        
 
835
 
832
836
        Implemented so that transformations can be made on keys,
833
837
        as well as on values. (used by encode and decode)
834
 
        
 
838
 
835
839
        Also renames comments.
836
840
        """
837
841
        if oldkey in self.scalars:
859
863
            call_on_sections=False, **keywargs):
860
864
        """
861
865
        Walk every member and call a function on the keyword and value.
862
 
        
 
866
 
863
867
        Return a dictionary of the return values
864
 
        
 
868
 
865
869
        If the function raises an exception, raise the errror
866
870
        unless ``raise_errors=False``, in which case set the return value to
867
871
        ``False``.
868
 
        
 
872
 
869
873
        Any unrecognised keyword arguments you pass to walk, will be pased on
870
874
        to the function you pass in.
871
 
        
 
875
 
872
876
        Note: if ``call_on_sections`` is ``True`` then - on encountering a
873
877
        subsection, *first* the function is called for the *whole* subsection,
874
878
        and then recurses into it's members. This means your function must be
875
879
        able to handle strings, dictionaries and lists. This allows you
876
880
        to change the key of subsections as well as for ordinary members. The
877
881
        return value when called on the whole subsection has to be discarded.
878
 
        
 
882
 
879
883
        See  the encode and decode methods for examples, including functions.
880
 
        
 
884
 
881
885
        .. admonition:: caution
882
 
        
 
886
 
883
887
            You can use ``walk`` to transform the names of members of a section
884
888
            but you mustn't add or delete members.
885
 
        
 
889
 
886
890
        >>> config = '''[XXXXsection]
887
891
        ... XXXXkey = XXXXvalue'''.splitlines()
888
892
        >>> cfg = ConfigObj(config)
945
949
        Accepts a key as input. The corresponding value must be a string or
946
950
        the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
947
951
        retain compatibility with Python 2.2.
948
 
        
949
 
        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
950
954
        ``True``.
951
 
        
952
 
        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
953
957
        ``False``.
954
 
        
 
958
 
955
959
        ``as_bool`` is not case sensitive.
956
 
        
 
960
 
957
961
        Any other input will raise a ``ValueError``.
958
 
        
 
962
 
959
963
        >>> a = ConfigObj()
960
964
        >>> a['a'] = 'fish'
961
965
        >>> a.as_bool('a')
987
991
    def as_int(self, key):
988
992
        """
989
993
        A convenience method which coerces the specified value to an integer.
990
 
        
 
994
 
991
995
        If the value is an invalid literal for ``int``, a ``ValueError`` will
992
996
        be raised.
993
 
        
 
997
 
994
998
        >>> a = ConfigObj()
995
999
        >>> a['a'] = 'fish'
996
1000
        >>> a.as_int('a')
1010
1014
    def as_float(self, key):
1011
1015
        """
1012
1016
        A convenience method which coerces the specified value to a float.
1013
 
        
 
1017
 
1014
1018
        If the value is an invalid literal for ``float``, a ``ValueError`` will
1015
1019
        be raised.
1016
 
        
 
1020
 
1017
1021
        >>> a = ConfigObj()
1018
1022
        >>> a['a'] = 'fish'
1019
1023
        >>> a.as_float('a')
1027
1031
        3.2000000000000002
1028
1032
        """
1029
1033
        return float(self[key])
1030
 
    
1031
 
    
 
1034
 
 
1035
 
1032
1036
    def as_list(self, key):
1033
1037
        """
1034
1038
        A convenience method which fetches the specified value, guaranteeing
1035
1039
        that it is a list.
1036
 
        
 
1040
 
1037
1041
        >>> a = ConfigObj()
1038
1042
        >>> a['a'] = 1
1039
1043
        >>> a.as_list('a')
1049
1053
        if isinstance(result, (tuple, list)):
1050
1054
            return list(result)
1051
1055
        return [result]
1052
 
        
 
1056
 
1053
1057
 
1054
1058
    def restore_default(self, key):
1055
1059
        """
1056
1060
        Restore (and return) default value for the specified key.
1057
 
        
 
1061
 
1058
1062
        This method will only work for a ConfigObj that was created
1059
1063
        with a configspec and has been validated.
1060
 
        
 
1064
 
1061
1065
        If there is no default value for this key, ``KeyError`` is raised.
1062
1066
        """
1063
1067
        default = self.default_values[key]
1066
1070
            self.defaults.append(key)
1067
1071
        return default
1068
1072
 
1069
 
    
 
1073
 
1070
1074
    def restore_defaults(self):
1071
1075
        """
1072
1076
        Recursively restore default values to all members
1073
1077
        that have them.
1074
 
        
 
1078
 
1075
1079
        This method will only work for a ConfigObj that was created
1076
1080
        with a configspec and has been validated.
1077
 
        
 
1081
 
1078
1082
        It doesn't delete or modify entries without default values.
1079
1083
        """
1080
1084
        for key in self.default_values:
1081
1085
            self.restore_default(key)
1082
 
            
 
1086
 
1083
1087
        for section in self.sections:
1084
1088
            self[section].restore_defaults()
1085
1089
 
1190
1194
    def __init__(self, infile=None, options=None, _inspec=False, **kwargs):
1191
1195
        """
1192
1196
        Parse a config file or create a config file object.
1193
 
        
 
1197
 
1194
1198
        ``ConfigObj(infile=None, options=None, **kwargs)``
1195
1199
        """
1196
1200
        self._inspec = _inspec
1197
1201
        # init the superclass
1198
1202
        Section.__init__(self, self, 0, self)
1199
 
        
 
1203
 
1200
1204
        infile = infile or []
1201
1205
        options = dict(options or {})
1202
 
            
 
1206
 
1203
1207
        # keyword arguments take precedence over an options dictionary
1204
1208
        options.update(kwargs)
1205
1209
        if _inspec:
1206
1210
            options['list_values'] = False
1207
 
        
 
1211
 
1208
1212
        defaults = OPTION_DEFAULTS.copy()
1209
1213
        # TODO: check the values too.
1210
1214
        for entry in options:
1211
1215
            if entry not in defaults:
1212
1216
                raise TypeError('Unrecognised option "%s".' % entry)
1213
 
        
 
1217
 
1214
1218
        # Add any explicit options to the defaults
1215
1219
        defaults.update(options)
1216
1220
        self._initialise(defaults)
1217
1221
        configspec = defaults['configspec']
1218
1222
        self._original_configspec = configspec
1219
1223
        self._load(infile, configspec)
1220
 
        
1221
 
        
 
1224
 
 
1225
 
1222
1226
    def _load(self, infile, configspec):
1223
1227
        if isinstance(infile, basestring):
1224
1228
            self.filename = infile
1238
1242
                    h.write('')
1239
1243
                    h.close()
1240
1244
                infile = []
1241
 
                
 
1245
 
1242
1246
        elif isinstance(infile, (list, tuple)):
1243
1247
            infile = list(infile)
1244
 
            
 
1248
 
1245
1249
        elif isinstance(infile, dict):
1246
1250
            # initialise self
1247
1251
            # the Section class handles creating subsections
1248
1252
            if isinstance(infile, ConfigObj):
1249
1253
                # get a copy of our ConfigObj
1250
1254
                infile = infile.dict()
1251
 
                
 
1255
 
1252
1256
            for entry in infile:
1253
1257
                self[entry] = infile[entry]
1254
1258
            del self._errors
1255
 
            
 
1259
 
1256
1260
            if configspec is not None:
1257
1261
                self._handle_configspec(configspec)
1258
1262
            else:
1259
1263
                self.configspec = None
1260
1264
            return
1261
 
        
 
1265
 
1262
1266
        elif getattr(infile, 'read', MISSING) is not MISSING:
1263
1267
            # This supports file like objects
1264
1268
            infile = infile.read() or []
1266
1270
            # in case it's not an 8 bit encoding
1267
1271
        else:
1268
1272
            raise TypeError('infile must be a filename, file like object, or list of lines.')
1269
 
        
 
1273
 
1270
1274
        if infile:
1271
1275
            # don't do it for the empty ConfigObj
1272
1276
            infile = self._handle_bom(infile)
1284
1288
                break
1285
1289
 
1286
1290
            infile = [line.rstrip('\r\n') for line in infile]
1287
 
            
 
1291
 
1288
1292
        self._parse(infile)
1289
1293
        # if we had any errors, now is the time to raise them
1290
1294
        if self._errors:
1302
1306
            raise error
1303
1307
        # delete private attributes
1304
1308
        del self._errors
1305
 
        
 
1309
 
1306
1310
        if configspec is None:
1307
1311
            self.configspec = None
1308
1312
        else:
1309
1313
            self._handle_configspec(configspec)
1310
 
    
1311
 
    
 
1314
 
 
1315
 
1312
1316
    def _initialise(self, options=None):
1313
1317
        if options is None:
1314
1318
            options = OPTION_DEFAULTS
1315
 
            
 
1319
 
1316
1320
        # initialise a few variables
1317
1321
        self.filename = None
1318
1322
        self._errors = []
1329
1333
        self.newlines = None
1330
1334
        self.write_empty_values = options['write_empty_values']
1331
1335
        self.unrepr = options['unrepr']
1332
 
        
 
1336
 
1333
1337
        self.initial_comment = []
1334
1338
        self.final_comment = []
1335
1339
        self.configspec = None
1336
 
        
 
1340
 
1337
1341
        if self._inspec:
1338
1342
            self.list_values = False
1339
 
        
 
1343
 
1340
1344
        # Clear section attributes as well
1341
1345
        Section._initialise(self)
1342
 
        
1343
 
        
 
1346
 
 
1347
 
1344
1348
    def __repr__(self):
1345
 
        return ('ConfigObj({%s})' % 
1346
 
                ', '.join([('%s: %s' % (repr(key), repr(self[key]))) 
 
1349
        return ('ConfigObj({%s})' %
 
1350
                ', '.join([('%s: %s' % (repr(key), repr(self[key])))
1347
1351
                for key in (self.scalars + self.sections)]))
1348
 
    
1349
 
    
 
1352
 
 
1353
 
1350
1354
    def _handle_bom(self, infile):
1351
1355
        """
1352
1356
        Handle any BOM, and decode if necessary.
1353
 
        
 
1357
 
1354
1358
        If an encoding is specified, that *must* be used - but the BOM should
1355
1359
        still be removed (and the BOM attribute set).
1356
 
        
 
1360
 
1357
1361
        (If the encoding is wrongly specified, then a BOM for an alternative
1358
1362
        encoding won't be discovered or removed.)
1359
 
        
 
1363
 
1360
1364
        If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1361
1365
        removed. The BOM attribute will be set. UTF16 will be decoded to
1362
1366
        unicode.
1363
 
        
 
1367
 
1364
1368
        NOTE: This method must not be called with an empty ``infile``.
1365
 
        
 
1369
 
1366
1370
        Specifying the *wrong* encoding is likely to cause a
1367
1371
        ``UnicodeDecodeError``.
1368
 
        
 
1372
 
1369
1373
        ``infile`` must always be returned as a list of lines, but may be
1370
1374
        passed in as a single string.
1371
1375
        """
1375
1379
            # the encoding specified doesn't have one
1376
1380
            # just decode
1377
1381
            return self._decode(infile, self.encoding)
1378
 
        
 
1382
 
1379
1383
        if isinstance(infile, (list, tuple)):
1380
1384
            line = infile[0]
1381
1385
        else:
1397
1401
                        ##self.BOM = True
1398
1402
                        # Don't need to remove BOM
1399
1403
                        return self._decode(infile, encoding)
1400
 
                    
 
1404
 
1401
1405
                # If we get this far, will *probably* raise a DecodeError
1402
1406
                # As it doesn't appear to start with a BOM
1403
1407
                return self._decode(infile, self.encoding)
1404
 
            
 
1408
 
1405
1409
            # Must be UTF8
1406
1410
            BOM = BOM_SET[enc]
1407
1411
            if not line.startswith(BOM):
1408
1412
                return self._decode(infile, self.encoding)
1409
 
            
 
1413
 
1410
1414
            newline = line[len(BOM):]
1411
 
            
 
1415
 
1412
1416
            # BOM removed
1413
1417
            if isinstance(infile, (list, tuple)):
1414
1418
                infile[0] = newline
1416
1420
                infile = newline
1417
1421
            self.BOM = True
1418
1422
            return self._decode(infile, self.encoding)
1419
 
        
 
1423
 
1420
1424
        # No encoding specified - so we need to check for UTF8/UTF16
1421
1425
        for BOM, (encoding, final_encoding) in BOMS.items():
1422
1426
            if not line.startswith(BOM):
1440
1444
                        return infile
1441
1445
                # UTF16 - have to decode
1442
1446
                return self._decode(infile, encoding)
1443
 
            
 
1447
 
1444
1448
        # No BOM discovered and no encoding specified, just return
1445
1449
        if isinstance(infile, basestring):
1446
1450
            # infile read from a file will be a single string
1459
1463
    def _decode(self, infile, encoding):
1460
1464
        """
1461
1465
        Decode infile to unicode. Using the specified encoding.
1462
 
        
 
1466
 
1463
1467
        if is a string, it also needs converting to a list.
1464
1468
        """
1465
1469
        if isinstance(infile, basestring):
1500
1504
        temp_list_values = self.list_values
1501
1505
        if self.unrepr:
1502
1506
            self.list_values = False
1503
 
            
 
1507
 
1504
1508
        comment_list = []
1505
1509
        done_start = False
1506
1510
        this_section = self
1507
1511
        maxline = len(infile) - 1
1508
1512
        cur_index = -1
1509
1513
        reset_comment = False
1510
 
        
 
1514
 
1511
1515
        while cur_index < maxline:
1512
1516
            if reset_comment:
1513
1517
                comment_list = []
1519
1523
                reset_comment = False
1520
1524
                comment_list.append(line)
1521
1525
                continue
1522
 
            
 
1526
 
1523
1527
            if not done_start:
1524
1528
                # preserve initial comment
1525
1529
                self.initial_comment = comment_list
1526
1530
                comment_list = []
1527
1531
                done_start = True
1528
 
                
 
1532
 
1529
1533
            reset_comment = True
1530
1534
            # first we check if it's a section marker
1531
1535
            mat = self._sectionmarker.match(line)
1539
1543
                    self._handle_error("Cannot compute the section depth at line %s.",
1540
1544
                                       NestingError, infile, cur_index)
1541
1545
                    continue
1542
 
                
 
1546
 
1543
1547
                if cur_depth < this_section.depth:
1544
1548
                    # the new section is dropping back to a previous level
1545
1549
                    try:
1558
1562
                else:
1559
1563
                    self._handle_error("Section too nested at line %s.",
1560
1564
                                       NestingError, infile, cur_index)
1561
 
                    
 
1565
 
1562
1566
                sect_name = self._unquote(sect_name)
1563
 
                if parent.has_key(sect_name):
 
1567
                if sect_name in parent:
1564
1568
                    self._handle_error('Duplicate section name at line %s.',
1565
1569
                                       DuplicateError, infile, cur_index)
1566
1570
                    continue
1567
 
                
 
1571
 
1568
1572
                # create the new section
1569
1573
                this_section = Section(
1570
1574
                    parent,
1638
1642
                            continue
1639
1643
                #
1640
1644
                key = self._unquote(key)
1641
 
                if this_section.has_key(key):
 
1645
                if key in this_section:
1642
1646
                    self._handle_error(
1643
1647
                        'Duplicate keyword name at line %s.',
1644
1648
                        DuplicateError, infile, cur_index)
1667
1671
        """
1668
1672
        Given a section and a depth level, walk back through the sections
1669
1673
        parents to see if the depth level matches a previous section.
1670
 
        
 
1674
 
1671
1675
        Return a reference to the right section,
1672
1676
        or raise a SyntaxError.
1673
1677
        """
1685
1689
    def _handle_error(self, text, ErrorClass, infile, cur_index):
1686
1690
        """
1687
1691
        Handle an error according to the error settings.
1688
 
        
 
1692
 
1689
1693
        Either raise the error or store it.
1690
1694
        The error will have occured at ``cur_index``
1691
1695
        """
1711
1715
    def _quote(self, value, multiline=True):
1712
1716
        """
1713
1717
        Return a safely quoted version of a value.
1714
 
        
 
1718
 
1715
1719
        Raise a ConfigObjError if the value cannot be safely quoted.
1716
1720
        If multiline is ``True`` (default) then use triple quotes
1717
1721
        if necessary.
1718
 
        
 
1722
 
1719
1723
        * Don't quote values that don't need it.
1720
1724
        * Recursively quote members of a list and return a comma joined list.
1721
1725
        * Multiline is ``False`` for lists.
1722
1726
        * Obey list syntax for empty and single member lists.
1723
 
        
 
1727
 
1724
1728
        If ``list_values=False`` then the value is only quoted if it contains
1725
1729
        a ``\\n`` (is multiline) or '#'.
1726
 
        
 
1730
 
1727
1731
        If ``write_empty_values`` is set, and the value is an empty string, it
1728
1732
        won't be quoted.
1729
1733
        """
1731
1735
            # Only if multiline is set, so that it is used for values not
1732
1736
            # keys, and not values that are part of a list
1733
1737
            return ''
1734
 
        
 
1738
 
1735
1739
        if multiline and isinstance(value, (list, tuple)):
1736
1740
            if not value:
1737
1741
                return ','
1747
1751
 
1748
1752
        if not value:
1749
1753
            return '""'
1750
 
        
 
1754
 
1751
1755
        no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1752
1756
        need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1753
1757
        hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1754
1758
        check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1755
 
        
 
1759
 
1756
1760
        if check_for_single:
1757
1761
            if not self.list_values:
1758
1762
                # we don't quote if ``list_values=False``
1770
1774
        else:
1771
1775
            # if value has '\n' or "'" *and* '"', it will need triple quotes
1772
1776
            quot = self._get_triple_quote(value)
1773
 
        
 
1777
 
1774
1778
        if quot == noquot and '#' in value and self.list_values:
1775
1779
            quot = self._get_single_quote(value)
1776
 
                
 
1780
 
1777
1781
        return quot % value
1778
 
    
1779
 
    
 
1782
 
 
1783
 
1780
1784
    def _get_single_quote(self, value):
1781
1785
        if ("'" in value) and ('"' in value):
1782
1786
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1785
1789
        else:
1786
1790
            quot = dquot
1787
1791
        return quot
1788
 
    
1789
 
    
 
1792
 
 
1793
 
1790
1794
    def _get_triple_quote(self, value):
1791
1795
        if (value.find('"""') != -1) and (value.find("'''") != -1):
1792
1796
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1793
1797
        if value.find('"""') == -1:
1794
1798
            quot = tdquot
1795
1799
        else:
1796
 
            quot = tsquot 
 
1800
            quot = tsquot
1797
1801
        return quot
1798
1802
 
1799
1803
 
1883
1887
 
1884
1888
    def _handle_configspec(self, configspec):
1885
1889
        """Parse the configspec."""
1886
 
        # FIXME: Should we check that the configspec was created with the 
 
1890
        # FIXME: Should we check that the configspec was created with the
1887
1891
        #        correct settings ? (i.e. ``list_values=False``)
1888
1892
        if not isinstance(configspec, ConfigObj):
1889
1893
            try:
1897
1901
                raise ConfigspecError('Parsing configspec failed: %s' % e)
1898
1902
            except IOError, e:
1899
1903
                raise IOError('Reading configspec failed: %s' % e)
1900
 
        
 
1904
 
1901
1905
        self.configspec = configspec
1902
 
            
1903
 
 
1904
 
        
 
1906
 
 
1907
 
 
1908
 
1905
1909
    def _set_configspec(self, section, copy):
1906
1910
        """
1907
1911
        Called by validate. Handles setting the configspec on subsections
1913
1917
            for entry in section.sections:
1914
1918
                if entry not in configspec:
1915
1919
                    section[entry].configspec = many
1916
 
                    
 
1920
 
1917
1921
        for entry in configspec.sections:
1918
1922
            if entry == '__many__':
1919
1923
                continue
1923
1927
                    # copy comments
1924
1928
                    section.comments[entry] = configspec.comments.get(entry, [])
1925
1929
                    section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1926
 
                
 
1930
 
1927
1931
            # Could be a scalar when we expect a section
1928
1932
            if isinstance(section[entry], Section):
1929
1933
                section[entry].configspec = configspec[entry]
1930
 
                        
 
1934
 
1931
1935
 
1932
1936
    def _write_line(self, indent_string, entry, this_entry, comment):
1933
1937
        """Write an individual line, for the write method"""
1967
1971
    def write(self, outfile=None, section=None):
1968
1972
        """
1969
1973
        Write the current ConfigObj as a file
1970
 
        
 
1974
 
1971
1975
        tekNico: FIXME: use StringIO instead of real files
1972
 
        
 
1976
 
1973
1977
        >>> filename = a.filename
1974
1978
        >>> a.filename = 'test.ini'
1975
1979
        >>> a.write()
1980
1984
        if self.indent_type is None:
1981
1985
            # this can be true if initialised from a dictionary
1982
1986
            self.indent_type = DEFAULT_INDENT_TYPE
1983
 
            
 
1987
 
1984
1988
        out = []
1985
1989
        cs = self._a_to_u('#')
1986
1990
        csp = self._a_to_u('# ')
1994
1998
                if stripped_line and not stripped_line.startswith(cs):
1995
1999
                    line = csp + line
1996
2000
                out.append(line)
1997
 
                
 
2001
 
1998
2002
        indent_string = self.indent_type * section.depth
1999
2003
        for entry in (section.scalars + section.sections):
2000
2004
            if entry in section.defaults:
2007
2011
                out.append(indent_string + comment_line)
2008
2012
            this_entry = section[entry]
2009
2013
            comment = self._handle_comment(section.inline_comments[entry])
2010
 
            
 
2014
 
2011
2015
            if isinstance(this_entry, dict):
2012
2016
                # a section
2013
2017
                out.append(self._write_marker(
2022
2026
                    entry,
2023
2027
                    this_entry,
2024
2028
                    comment))
2025
 
                
 
2029
 
2026
2030
        if section is self:
2027
2031
            for line in self.final_comment:
2028
2032
                line = self._decode_element(line)
2031
2035
                    line = csp + line
2032
2036
                out.append(line)
2033
2037
            self.interpolation = int_val
2034
 
            
 
2038
 
2035
2039
        if section is not self:
2036
2040
            return out
2037
 
        
 
2041
 
2038
2042
        if (self.filename is None) and (outfile is None):
2039
2043
            # output a list of lines
2040
2044
            # might need to encode
2048
2052
                    out.append('')
2049
2053
                out[0] = BOM_UTF8 + out[0]
2050
2054
            return out
2051
 
        
 
2055
 
2052
2056
        # Turn the list to a string, joined with correct newlines
2053
2057
        newline = self.newlines or os.linesep
2054
2058
        output = self._a_to_u(newline).join(out)
2057
2061
        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2058
2062
            # Add the UTF8 BOM
2059
2063
            output = BOM_UTF8 + output
2060
 
            
 
2064
 
2061
2065
        if not output.endswith(newline):
2062
2066
            output += newline
2063
2067
        if outfile is not None:
2072
2076
                 section=None):
2073
2077
        """
2074
2078
        Test the ConfigObj against a configspec.
2075
 
        
 
2079
 
2076
2080
        It uses the ``validator`` object from *validate.py*.
2077
 
        
 
2081
 
2078
2082
        To run ``validate`` on the current ConfigObj, call: ::
2079
 
        
 
2083
 
2080
2084
            test = config.validate(validator)
2081
 
        
 
2085
 
2082
2086
        (Normally having previously passed in the configspec when the ConfigObj
2083
2087
        was created - you can dynamically assign a dictionary of checks to the
2084
2088
        ``configspec`` attribute of a section though).
2085
 
        
 
2089
 
2086
2090
        It returns ``True`` if everything passes, or a dictionary of
2087
2091
        pass/fails (True/False). If every member of a subsection passes, it
2088
2092
        will just have the value ``True``. (It also returns ``False`` if all
2089
2093
        members fail).
2090
 
        
 
2094
 
2091
2095
        In addition, it converts the values from strings to their native
2092
2096
        types if their checks pass (and ``stringify`` is set).
2093
 
        
 
2097
 
2094
2098
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2095
2099
        of a marking a fail with a ``False``, it will preserve the actual
2096
2100
        exception object. This can contain info about the reason for failure.
2097
2101
        For example the ``VdtValueTooSmallError`` indicates that the value
2098
2102
        supplied was too small. If a value (or section) is missing it will
2099
2103
        still be marked as ``False``.
2100
 
        
 
2104
 
2101
2105
        You must have the validate module to use ``preserve_errors=True``.
2102
 
        
 
2106
 
2103
2107
        You can then use the ``flatten_errors`` function to turn your nested
2104
2108
        results dictionary into a flattened list of failures - useful for
2105
2109
        displaying meaningful error messages.
2112
2116
                # Which makes importing configobj faster
2113
2117
                from validate import VdtMissingValue
2114
2118
                self._vdtMissingValue = VdtMissingValue
2115
 
                
 
2119
 
2116
2120
            section = self
2117
2121
 
2118
2122
            if copy:
2122
2126
                section.BOM = section.configspec.BOM
2123
2127
                section.newlines = section.configspec.newlines
2124
2128
                section.indent_type = section.configspec.indent_type
2125
 
            
 
2129
 
2126
2130
        #
2127
2131
        configspec = section.configspec
2128
2132
        self._set_configspec(section, copy)
2129
 
        
 
2133
 
2130
2134
        def validate_entry(entry, spec, val, missing, ret_true, ret_false):
2131
2135
            try:
2132
2136
                check = validator.check(spec,
2142
2146
                    ret_false = False
2143
2147
                ret_true = False
2144
2148
            else:
2145
 
                try: 
 
2149
                try:
2146
2150
                    section.default_values.pop(entry, None)
2147
 
                except AttributeError: 
 
2151
                except AttributeError:
2148
2152
                    # For Python 2.2 compatibility
2149
2153
                    try:
2150
2154
                        del section.default_values[entry]
2151
2155
                    except KeyError:
2152
2156
                        pass
2153
 
                    
2154
 
                try: 
 
2157
 
 
2158
                try:
2155
2159
                    section.default_values[entry] = validator.get_default_value(configspec[entry])
2156
2160
                except (KeyError, AttributeError):
2157
2161
                    # No default or validator has no 'get_default_value' (e.g. SimpleVal)
2158
2162
                    pass
2159
 
                    
 
2163
 
2160
2164
                ret_false = False
2161
2165
                out[entry] = True
2162
2166
                if self.stringify or missing:
2176
2180
                if not copy and missing and entry not in section.defaults:
2177
2181
                    section.defaults.append(entry)
2178
2182
            return ret_true, ret_false
2179
 
        
 
2183
 
2180
2184
        #
2181
2185
        out = {}
2182
2186
        ret_true = True
2183
2187
        ret_false = True
2184
 
        
 
2188
 
2185
2189
        unvalidated = [k for k in section.scalars if k not in configspec]
2186
 
        incorrect_sections = [k for k in configspec.sections if k in section.scalars]        
 
2190
        incorrect_sections = [k for k in configspec.sections if k in section.scalars]
2187
2191
        incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2188
 
        
 
2192
 
2189
2193
        for entry in configspec.scalars:
2190
2194
            if entry in ('__many__', '___many___'):
2191
2195
                # reserved names
2192
2196
                continue
2193
 
            
 
2197
 
2194
2198
            if (not entry in section.scalars) or (entry in section.defaults):
2195
2199
                # missing entries
2196
2200
                # or entries from defaults
2206
2210
            else:
2207
2211
                missing = False
2208
2212
                val = section[entry]
2209
 
                
2210
 
            ret_true, ret_false = validate_entry(entry, configspec[entry], val, 
 
2213
 
 
2214
            ret_true, ret_false = validate_entry(entry, configspec[entry], val,
2211
2215
                                                 missing, ret_true, ret_false)
2212
 
        
 
2216
 
2213
2217
        many = None
2214
2218
        if '__many__' in configspec.scalars:
2215
2219
            many = configspec['__many__']
2216
2220
        elif '___many___' in configspec.scalars:
2217
2221
            many = configspec['___many___']
2218
 
        
 
2222
 
2219
2223
        if many is not None:
2220
2224
            for entry in unvalidated:
2221
2225
                val = section[entry]
2238
2242
                ret_false = False
2239
2243
                msg = 'Section %r was provided as a single value' % entry
2240
2244
                out[entry] = validator.baseErrorClass(msg)
2241
 
                
 
2245
 
2242
2246
        # Missing sections will have been created as empty ones when the
2243
2247
        # configspec was read.
2244
2248
        for entry in section.sections:
2276
2280
        self.configspec = None
2277
2281
        # Just to be sure ;-)
2278
2282
        self._original_configspec = None
2279
 
        
2280
 
        
 
2283
 
 
2284
 
2281
2285
    def reload(self):
2282
2286
        """
2283
2287
        Reload a ConfigObj from file.
2284
 
        
 
2288
 
2285
2289
        This method raises a ``ReloadError`` if the ConfigObj doesn't have
2286
2290
        a filename attribute pointing to a file.
2287
2291
        """
2294
2298
            if entry == 'configspec':
2295
2299
                continue
2296
2300
            current_options[entry] = getattr(self, entry)
2297
 
            
 
2301
 
2298
2302
        configspec = self._original_configspec
2299
2303
        current_options['configspec'] = configspec
2300
 
            
 
2304
 
2301
2305
        self.clear()
2302
2306
        self._initialise(current_options)
2303
2307
        self._load(filename, configspec)
2304
 
        
 
2308
 
2305
2309
 
2306
2310
 
2307
2311
class SimpleVal(object):
2308
2312
    """
2309
2313
    A simple validator.
2310
2314
    Can be used to check that all members expected are present.
2311
 
    
 
2315
 
2312
2316
    To use it, provide a configspec with all your members in (the value given
2313
2317
    will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2314
2318
    method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2315
2319
    members are present, or a dictionary with True/False meaning
2316
2320
    present/missing. (Whole missing sections will be replaced with ``False``)
2317
2321
    """
2318
 
    
 
2322
 
2319
2323
    def __init__(self):
2320
2324
        self.baseErrorClass = ConfigObjError
2321
 
    
 
2325
 
2322
2326
    def check(self, check, member, missing=False):
2323
2327
        """A dummy check method, always returns the value unchanged."""
2324
2328
        if missing:
2331
2335
    """
2332
2336
    An example function that will turn a nested dictionary of results
2333
2337
    (as returned by ``ConfigObj.validate``) into a flat list.
2334
 
    
 
2338
 
2335
2339
    ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2336
2340
    dictionary returned by ``validate``.
2337
 
    
 
2341
 
2338
2342
    (This is a recursive function, so you shouldn't use the ``levels`` or
2339
2343
    ``results`` arguments - they are used by the function.)
2340
 
    
 
2344
 
2341
2345
    Returns a list of keys that failed. Each member of the list is a tuple :
2342
 
    
 
2346
 
2343
2347
    ::
2344
 
    
 
2348
 
2345
2349
        ([list of sections...], key, result)
2346
 
    
 
2350
 
2347
2351
    If ``validate`` was called with ``preserve_errors=False`` (the default)
2348
2352
    then ``result`` will always be ``False``.
2349
2353
 
2350
2354
    *list of sections* is a flattened list of sections that the key was found
2351
2355
    in.
2352
 
    
 
2356
 
2353
2357
    If the section was missing (or a section was expected and a scalar provided
2354
2358
    - or vice-versa) then key will be ``None``.
2355
 
    
 
2359
 
2356
2360
    If the value (or section) was missing then ``result`` will be ``False``.
2357
 
    
 
2361
 
2358
2362
    If ``validate`` was called with ``preserve_errors=True`` and a value
2359
2363
    was present, but failed the check, then ``result`` will be the exception
2360
2364
    object returned. You can use this as a string that describes the failure.
2361
 
    
 
2365
 
2362
2366
    For example *The value "3" is of the wrong type*.
2363
 
    
 
2367
 
2364
2368
    >>> import validate
2365
2369
    >>> vtor = validate.Validator()
2366
2370
    >>> my_ini = '''