~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Aaron Bentley
  • Date: 2007-12-09 23:53:50 UTC
  • mto: This revision was merged to the branch mainline in revision 3133.
  • Revision ID: aaron.bentley@utoronto.ca-20071209235350-qp39yk0xzx7a4f6p
Don't use the base if not cherrypicking

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-2006 Michael Foord, Nicola Larosa
4
4
# E-mail: fuzzyman AT voidspace DOT org DOT uk
5
5
#         nico AT tekNico DOT net
6
6
 
25
25
 
26
26
import os, re
27
27
compiler = None
28
 
# Bzr modification: Disabled import of 'compiler' module
29
 
# bzr doesn't use the 'unrepr' feature of configobj, so importing compiler just
30
 
# wastes several milliseconds on every single bzr invocation.
31
 
#   -- Andrew Bennetts, 2008-10-14
32
 
#try:
33
 
#    import compiler
34
 
#except ImportError:
35
 
#    # for IronPython
36
 
#    pass
 
28
try:
 
29
    import compiler
 
30
except ImportError:
 
31
    # for IronPython
 
32
    pass
37
33
from types import StringTypes
38
34
from warnings import warn
39
35
try:
92
88
    None: BOM_UTF8
93
89
    }
94
90
 
95
 
 
96
 
def match_utf8(encoding):
97
 
    return BOM_LIST.get(encoding.lower()) == 'utf_8'
98
 
 
99
 
 
100
 
# Quote strings used for writing values
101
 
squot = "'%s'"
102
 
dquot = '"%s"'
103
 
noquot = "%s"
104
 
wspace_plus = ' \r\t\n\v\t\'"'
105
 
tsquot = '"""%s"""'
106
 
tdquot = "'''%s'''"
 
91
try:
 
92
    from validate import VdtMissingValue
 
93
except ImportError:
 
94
    VdtMissingValue = None
107
95
 
108
96
try:
109
97
    enumerate
121
109
    True, False = 1, 0
122
110
 
123
111
 
124
 
__version__ = '4.5.2'
 
112
__version__ = '4.4.0'
125
113
 
126
114
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
127
115
 
142
130
    'InterpolationLoopError',
143
131
    'MissingInterpolationOption',
144
132
    'RepeatSectionError',
145
 
    'ReloadError',
146
133
    'UnreprError',
147
134
    'UnknownType',
148
135
    '__docformat__',
170
157
}
171
158
 
172
159
 
173
 
 
174
160
def getObj(s):
175
161
    s = "a=" + s
176
162
    if compiler is None:
178
164
    p = compiler.parse(s)
179
165
    return p.getChildren()[1].getChildren()[0].getChildren()[1]
180
166
 
181
 
 
182
167
class UnknownType(Exception):
183
168
    pass
184
169
 
185
 
 
186
 
class Builder(object):
 
170
class Builder:
187
171
    
188
172
    def build(self, o):
189
173
        m = getattr(self, 'build_' + o.__class__.__name__, None)
215
199
        if o.name == 'False':
216
200
            return False
217
201
        
218
 
        # An undefined Name
 
202
        # An undefinted Name
219
203
        raise UnknownType('Undefined Name')
220
204
    
221
205
    def build_Add(self, o):
238
222
    def build_UnaryAdd(self, o):
239
223
        return self.build_Const(o.getChildren()[0])
240
224
 
241
 
 
242
 
_builder = Builder()
243
 
 
244
 
 
245
225
def unrepr(s):
246
226
    if not s:
247
227
        return s
248
 
    return _builder.build(getObj(s))
249
 
 
250
 
 
 
228
    return Builder().build(getObj(s))
 
229
 
 
230
def _splitlines(instring):
 
231
    """Split a string on lines, without losing line endings or truncating."""
 
232
    
251
233
 
252
234
class ConfigObjError(SyntaxError):
253
235
    """
254
236
    This is the base class for all errors that ConfigObj raises.
255
237
    It is a subclass of SyntaxError.
256
238
    """
257
 
    def __init__(self, msg='', line_number=None, line=''):
 
239
    def __init__(self, message='', line_number=None, line=''):
258
240
        self.line = line
259
241
        self.line_number = line_number
260
 
        self.msg = msg
261
 
        SyntaxError.__init__(self, msg)
262
 
 
 
242
        self.message = message
 
243
        SyntaxError.__init__(self, message)
263
244
 
264
245
class NestingError(ConfigObjError):
265
246
    """
266
247
    This error indicates a level of nesting that doesn't match.
267
248
    """
268
249
 
269
 
 
270
250
class ParseError(ConfigObjError):
271
251
    """
272
252
    This error indicates that a line is badly written.
274
254
    nor a valid section marker line.
275
255
    """
276
256
 
277
 
 
278
 
class ReloadError(IOError):
279
 
    """
280
 
    A 'reload' operation failed.
281
 
    This exception is a subclass of ``IOError``.
282
 
    """
283
 
    def __init__(self):
284
 
        IOError.__init__(self, 'reload failed, filename is not set.')
285
 
 
286
 
 
287
257
class DuplicateError(ConfigObjError):
288
258
    """
289
259
    The keyword or section specified already exists.
290
260
    """
291
261
 
292
 
 
293
262
class ConfigspecError(ConfigObjError):
294
263
    """
295
264
    An error occured whilst parsing a configspec.
296
265
    """
297
266
 
298
 
 
299
267
class InterpolationError(ConfigObjError):
300
268
    """Base class for the two interpolation errors."""
301
269
 
302
 
 
303
270
class InterpolationLoopError(InterpolationError):
304
271
    """Maximum interpolation depth exceeded in string interpolation."""
305
272
 
308
275
            self,
309
276
            'interpolation loop detected in value "%s".' % option)
310
277
 
311
 
 
312
278
class RepeatSectionError(ConfigObjError):
313
279
    """
314
280
    This error indicates additional sections in a section with a
315
281
    ``__many__`` (repeated) section.
316
282
    """
317
283
 
318
 
 
319
284
class MissingInterpolationOption(InterpolationError):
320
285
    """A value specified for interpolation was missing."""
321
286
 
324
289
            self,
325
290
            'missing option "%s" in interpolation.' % option)
326
291
 
327
 
 
328
292
class UnreprError(ConfigObjError):
329
293
    """An error parsing in unrepr mode."""
330
294
 
331
295
 
332
 
 
333
296
class InterpolationEngine(object):
334
297
    """
335
298
    A helper class to help perform string interpolation.
345
308
        # the Section instance that "owns" this engine
346
309
        self.section = section
347
310
 
348
 
 
349
311
    def interpolate(self, key, value):
350
312
        def recursive_interpolate(key, value, section, backtrail):
351
313
            """The function that does the actual work.
394
356
        value = recursive_interpolate(key, value, self.section, {})
395
357
        return value
396
358
 
397
 
 
398
359
    def _fetch(self, key):
399
360
        """Helper function to fetch values from owning section.
400
361
 
428
389
            raise MissingInterpolationOption(key)
429
390
        return val, current_section
430
391
 
431
 
 
432
392
    def _parse_match(self, match):
433
393
        """Implementation-dependent helper function.
434
394
 
445
405
        interpolation should be performed on the resulting value
446
406
        (e.g., if we interpolated "$$" and returned "$").
447
407
        """
448
 
        raise NotImplementedError()
 
408
        raise NotImplementedError
449
409
    
450
410
 
451
 
 
452
411
class ConfigParserInterpolation(InterpolationEngine):
453
412
    """Behaves like ConfigParser."""
454
413
    _KEYCRE = re.compile(r"%\(([^)]*)\)s")
459
418
        return key, value, section
460
419
 
461
420
 
462
 
 
463
421
class TemplateInterpolation(InterpolationEngine):
464
422
    """Behaves like string.Template."""
465
423
    _delimiter = '$'
484
442
        # Anything else: ignore completely, just return it unchanged
485
443
        return None, match.group(), None
486
444
 
487
 
 
488
445
interpolation_engines = {
489
446
    'configparser': ConfigParserInterpolation,
490
447
    'template': TemplateInterpolation,
491
448
}
492
449
 
493
 
 
494
 
 
495
450
class Section(dict):
496
451
    """
497
452
    A dictionary-like object that represents a section in a config file.
526
481
        self.main = main
527
482
        # level of nesting depth of this Section
528
483
        self.depth = depth
 
484
        # the sequence of scalar values in this Section
 
485
        self.scalars = []
 
486
        # the sequence of sections in this Section
 
487
        self.sections = []
529
488
        # purely for information
530
489
        self.name = name
531
 
        #
532
 
        self._initialise()
533
 
        # we do this explicitly so that __setitem__ is used properly
534
 
        # (rather than just passing to ``dict.__init__``)
535
 
        for entry, value in indict.iteritems():
536
 
            self[entry] = value
537
 
            
538
 
            
539
 
    def _initialise(self):
540
 
        # the sequence of scalar values in this Section
541
 
        self.scalars = []
542
 
        # the sequence of sections in this Section
543
 
        self.sections = []
544
490
        # for comments :-)
545
491
        self.comments = {}
546
492
        self.inline_comments = {}
553
499
        self._cs_section_inline_comments = {}
554
500
        # for defaults
555
501
        self.defaults = []
556
 
        self.default_values = {}
557
 
 
 
502
        #
 
503
        # we do this explicitly so that __setitem__ is used properly
 
504
        # (rather than just passing to ``dict.__init__``)
 
505
        for entry in indict:
 
506
            self[entry] = indict[entry]
558
507
 
559
508
    def _interpolate(self, key, value):
560
509
        try:
578
527
        # let the engine do the actual work
579
528
        return engine.interpolate(key, value)
580
529
 
581
 
 
582
530
    def __getitem__(self, key):
583
531
        """Fetch the item and do string interpolation."""
584
532
        val = dict.__getitem__(self, key)
586
534
            return self._interpolate(key, val)
587
535
        return val
588
536
 
589
 
 
590
537
    def __setitem__(self, key, value, unrepr=False):
591
538
        """
592
539
        Correctly set a value.
602
549
        creating a new sub-section.
603
550
        """
604
551
        if not isinstance(key, StringTypes):
605
 
            raise ValueError('The key "%s" is not a string.' % key)
606
 
        
 
552
            raise ValueError, 'The key "%s" is not a string.' % key
607
553
        # add the comment
608
554
        if not self.comments.has_key(key):
609
555
            self.comments[key] = []
640
586
                elif isinstance(value, (list, tuple)):
641
587
                    for entry in value:
642
588
                        if not isinstance(entry, StringTypes):
643
 
                            raise TypeError('Value is not a string "%s".' % entry)
 
589
                            raise TypeError, (
 
590
                                'Value is not a string "%s".' % entry)
644
591
                else:
645
 
                    raise TypeError('Value is not a string "%s".' % value)
 
592
                    raise TypeError, 'Value is not a string "%s".' % value
646
593
            dict.__setitem__(self, key, value)
647
594
 
648
 
 
649
595
    def __delitem__(self, key):
650
596
        """Remove items from the sequence when deleting."""
651
597
        dict. __delitem__(self, key)
656
602
        del self.comments[key]
657
603
        del self.inline_comments[key]
658
604
 
659
 
 
660
605
    def get(self, key, default=None):
661
606
        """A version of ``get`` that doesn't bypass string interpolation."""
662
607
        try:
664
609
        except KeyError:
665
610
            return default
666
611
 
667
 
 
668
612
    def update(self, indict):
669
613
        """
670
614
        A version of update that uses our ``__setitem__``.
672
616
        for entry in indict:
673
617
            self[entry] = indict[entry]
674
618
 
675
 
 
676
619
    def pop(self, key, *args):
677
 
        """
678
 
        'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
679
 
        If key is not found, d is returned if given, otherwise KeyError is raised'
680
 
        """
 
620
        """ """
681
621
        val = dict.pop(self, key, *args)
682
622
        if key in self.scalars:
683
623
            del self.comments[key]
691
631
            return self._interpolate(key, val)
692
632
        return val
693
633
 
694
 
 
695
634
    def popitem(self):
696
635
        """Pops the first (key,val)"""
697
636
        sequence = (self.scalars + self.sections)
698
637
        if not sequence:
699
 
            raise KeyError(": 'popitem(): dictionary is empty'")
 
638
            raise KeyError, ": 'popitem(): dictionary is empty'"
700
639
        key = sequence[0]
701
640
        val =  self[key]
702
641
        del self[key]
703
642
        return key, val
704
643
 
705
 
 
706
644
    def clear(self):
707
645
        """
708
646
        A version of clear that also affects scalars/sections
718
656
        self.inline_comments = {}
719
657
        self.configspec = {}
720
658
 
721
 
 
722
659
    def setdefault(self, key, default=None):
723
660
        """A version of setdefault that sets sequence if appropriate."""
724
661
        try:
727
664
            self[key] = default
728
665
            return self[key]
729
666
 
730
 
 
731
667
    def items(self):
732
 
        """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
 
668
        """ """
733
669
        return zip((self.scalars + self.sections), self.values())
734
670
 
735
 
 
736
671
    def keys(self):
737
 
        """D.keys() -> list of D's keys"""
 
672
        """ """
738
673
        return (self.scalars + self.sections)
739
674
 
740
 
 
741
675
    def values(self):
742
 
        """D.values() -> list of D's values"""
 
676
        """ """
743
677
        return [self[key] for key in (self.scalars + self.sections)]
744
678
 
745
 
 
746
679
    def iteritems(self):
747
 
        """D.iteritems() -> an iterator over the (key, value) items of D"""
 
680
        """ """
748
681
        return iter(self.items())
749
682
 
750
 
 
751
683
    def iterkeys(self):
752
 
        """D.iterkeys() -> an iterator over the keys of D"""
 
684
        """ """
753
685
        return iter((self.scalars + self.sections))
754
686
 
755
687
    __iter__ = iterkeys
756
688
 
757
 
 
758
689
    def itervalues(self):
759
 
        """D.itervalues() -> an iterator over the values of D"""
 
690
        """ """
760
691
        return iter(self.values())
761
692
 
762
 
 
763
693
    def __repr__(self):
764
 
        """x.__repr__() <==> repr(x)"""
765
694
        return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
766
695
            for key in (self.scalars + self.sections)])
767
696
 
768
697
    __str__ = __repr__
769
 
    __str__.__doc__ = "x.__str__() <==> str(x)"
770
 
 
771
698
 
772
699
    # Extra methods - not in a normal dictionary
773
700
 
798
725
            newdict[entry] = this_entry
799
726
        return newdict
800
727
 
801
 
 
802
728
    def merge(self, indict):
803
729
        """
804
730
        A recursive update - useful for merging config files.
825
751
            else:   
826
752
                self[key] = val
827
753
 
828
 
 
829
754
    def rename(self, oldkey, newkey):
830
755
        """
831
756
        Change a keyname to another, without changing position in sequence.
840
765
        elif oldkey in self.sections:
841
766
            the_list = self.sections
842
767
        else:
843
 
            raise KeyError('Key "%s" not found.' % oldkey)
 
768
            raise KeyError, 'Key "%s" not found.' % oldkey
844
769
        pos = the_list.index(oldkey)
845
770
        #
846
771
        val = self[oldkey]
855
780
        self.comments[newkey] = comm
856
781
        self.inline_comments[newkey] = inline_comment
857
782
 
858
 
 
859
783
    def walk(self, function, raise_errors=True,
860
784
            call_on_sections=False, **keywargs):
861
785
        """
940
864
                **keywargs)
941
865
        return out
942
866
 
943
 
 
944
867
    def decode(self, encoding):
945
868
        """
946
869
        Decode all strings and values to unicode, using the specified encoding.
984
907
        # using ``call_on_sections`` allows us to modify section names
985
908
        self.walk(decode, call_on_sections=True)
986
909
 
987
 
 
988
910
    def encode(self, encoding):
989
911
        """
990
912
        Encode all strings and values from unicode,
1010
932
            section[newkey] = newval
1011
933
        self.walk(encode, call_on_sections=True)
1012
934
 
1013
 
 
1014
935
    def istrue(self, key):
1015
936
        """A deprecated version of ``as_bool``."""
1016
937
        warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
1017
938
                'instead.', DeprecationWarning)
1018
939
        return self.as_bool(key)
1019
940
 
1020
 
 
1021
941
    def as_bool(self, key):
1022
942
        """
1023
943
        Accepts a key as input. The corresponding value must be a string or
1054
974
        else:
1055
975
            try:
1056
976
                if not isinstance(val, StringTypes):
1057
 
                    # TODO: Why do we raise a KeyError here?
1058
 
                    raise KeyError()
 
977
                    raise KeyError
1059
978
                else:
1060
979
                    return self.main._bools[val.lower()]
1061
980
            except KeyError:
1062
981
                raise ValueError('Value "%s" is neither True nor False' % val)
1063
982
 
1064
 
 
1065
983
    def as_int(self, key):
1066
984
        """
1067
985
        A convenience method which coerces the specified value to an integer.
1084
1002
        """
1085
1003
        return int(self[key])
1086
1004
 
1087
 
 
1088
1005
    def as_float(self, key):
1089
1006
        """
1090
1007
        A convenience method which coerces the specified value to a float.
1105
1022
        3.2000000000000002
1106
1023
        """
1107
1024
        return float(self[key])
1108
 
 
1109
 
 
1110
 
    def restore_default(self, key):
1111
 
        """
1112
 
        Restore (and return) default value for the specified key.
1113
 
        
1114
 
        This method will only work for a ConfigObj that was created
1115
 
        with a configspec and has been validated.
1116
 
        
1117
 
        If there is no default value for this key, ``KeyError`` is raised.
1118
 
        """
1119
 
        default = self.default_values[key]
1120
 
        dict.__setitem__(self, key, default)
1121
 
        if key not in self.defaults:
1122
 
            self.defaults.append(key)
1123
 
        return default
1124
 
 
1125
1025
    
1126
 
    def restore_defaults(self):
1127
 
        """
1128
 
        Recursively restore default values to all members
1129
 
        that have them.
1130
 
        
1131
 
        This method will only work for a ConfigObj that was created
1132
 
        with a configspec and has been validated.
1133
 
        
1134
 
        It doesn't delete or modify entries without default values.
1135
 
        """
1136
 
        for key in self.default_values:
1137
 
            self.restore_default(key)
1138
 
            
1139
 
        for section in self.sections:
1140
 
            self[section].restore_defaults()
1141
 
 
1142
1026
 
1143
1027
class ConfigObj(Section):
1144
1028
    """An object to read, create, and write config files."""
1242
1126
        'true': True, 'false': False,
1243
1127
        }
1244
1128
 
1245
 
 
1246
1129
    def __init__(self, infile=None, options=None, **kwargs):
1247
1130
        """
1248
 
        Parse a config file or create a config file object.
 
1131
        Parse or create a config file object.
1249
1132
        
1250
1133
        ``ConfigObj(infile=None, options=None, **kwargs)``
1251
1134
        """
1252
 
        # init the superclass
1253
 
        Section.__init__(self, self, 0, self)
1254
 
        
1255
1135
        if infile is None:
1256
1136
            infile = []
1257
1137
        if options is None:
1258
1138
            options = {}
1259
1139
        else:
1260
1140
            options = dict(options)
1261
 
            
1262
1141
        # keyword arguments take precedence over an options dictionary
1263
1142
        options.update(kwargs)
1264
 
        
 
1143
        # init the superclass
 
1144
        Section.__init__(self, self, 0, self)
 
1145
        #
1265
1146
        defaults = OPTION_DEFAULTS.copy()
 
1147
        for entry in options.keys():
 
1148
            if entry not in defaults.keys():
 
1149
                raise TypeError, 'Unrecognised option "%s".' % entry
1266
1150
        # TODO: check the values too.
1267
 
        for entry in options:
1268
 
            if entry not in defaults:
1269
 
                raise TypeError('Unrecognised option "%s".' % entry)
1270
 
        
 
1151
        #
1271
1152
        # Add any explicit options to the defaults
1272
1153
        defaults.update(options)
1273
 
        self._initialise(defaults)
1274
 
        configspec = defaults['configspec']
1275
 
        self._original_configspec = configspec
1276
 
        self._load(infile, configspec)
1277
 
        
1278
 
        
1279
 
    def _load(self, infile, configspec):
 
1154
        #
 
1155
        # initialise a few variables
 
1156
        self.filename = None
 
1157
        self._errors = []
 
1158
        self.raise_errors = defaults['raise_errors']
 
1159
        self.interpolation = defaults['interpolation']
 
1160
        self.list_values = defaults['list_values']
 
1161
        self.create_empty = defaults['create_empty']
 
1162
        self.file_error = defaults['file_error']
 
1163
        self.stringify = defaults['stringify']
 
1164
        self.indent_type = defaults['indent_type']
 
1165
        self.encoding = defaults['encoding']
 
1166
        self.default_encoding = defaults['default_encoding']
 
1167
        self.BOM = False
 
1168
        self.newlines = None
 
1169
        self.write_empty_values = defaults['write_empty_values']
 
1170
        self.unrepr = defaults['unrepr']
 
1171
        #
 
1172
        self.initial_comment = []
 
1173
        self.final_comment = []
 
1174
        #
 
1175
        self._terminated = False
 
1176
        #
1280
1177
        if isinstance(infile, StringTypes):
1281
1178
            self.filename = infile
1282
1179
            if os.path.isfile(infile):
1283
 
                h = open(infile, 'rb')
1284
 
                infile = h.read() or []
1285
 
                h.close()
 
1180
                infile = open(infile).read() or []
1286
1181
            elif self.file_error:
1287
1182
                # raise an error if the file doesn't exist
1288
 
                raise IOError('Config file not found: "%s".' % self.filename)
 
1183
                raise IOError, 'Config file not found: "%s".' % self.filename
1289
1184
            else:
1290
1185
                # file doesn't already exist
1291
1186
                if self.create_empty:
1292
1187
                    # this is a good test that the filename specified
1293
 
                    # isn't impossible - like on a non-existent device
 
1188
                    # isn't impossible - like on a non existent device
1294
1189
                    h = open(infile, 'w')
1295
1190
                    h.write('')
1296
1191
                    h.close()
1297
1192
                infile = []
1298
 
                
1299
1193
        elif isinstance(infile, (list, tuple)):
1300
1194
            infile = list(infile)
1301
 
            
1302
1195
        elif isinstance(infile, dict):
1303
1196
            # initialise self
1304
1197
            # the Section class handles creating subsections
1305
1198
            if isinstance(infile, ConfigObj):
1306
1199
                # get a copy of our ConfigObj
1307
1200
                infile = infile.dict()
1308
 
                
1309
1201
            for entry in infile:
1310
1202
                self[entry] = infile[entry]
1311
1203
            del self._errors
1312
 
            
1313
 
            if configspec is not None:
1314
 
                self._handle_configspec(configspec)
 
1204
            if defaults['configspec'] is not None:
 
1205
                self._handle_configspec(defaults['configspec'])
1315
1206
            else:
1316
1207
                self.configspec = None
1317
1208
            return
1318
 
        
1319
 
        elif getattr(infile, 'read', None) is not None:
 
1209
        elif hasattr(infile, 'read'):
1320
1210
            # This supports file like objects
1321
1211
            infile = infile.read() or []
1322
1212
            # needs splitting into lines - but needs doing *after* decoding
1323
1213
            # in case it's not an 8 bit encoding
1324
1214
        else:
1325
 
            raise TypeError('infile must be a filename, file like object, or list of lines.')
1326
 
        
 
1215
            raise TypeError, ('infile must be a filename,'
 
1216
                ' file like object, or list of lines.')
 
1217
        #
1327
1218
        if infile:
1328
1219
            # don't do it for the empty ConfigObj
1329
1220
            infile = self._handle_bom(infile)
1339
1230
                        self.newlines = end
1340
1231
                        break
1341
1232
                break
1342
 
 
 
1233
            if infile[-1] and infile[-1] in ('\r', '\n', '\r\n'):
 
1234
                self._terminated = True
1343
1235
            infile = [line.rstrip('\r\n') for line in infile]
1344
 
            
 
1236
        #
1345
1237
        self._parse(infile)
1346
1238
        # if we had any errors, now is the time to raise them
1347
1239
        if self._errors:
1348
1240
            info = "at line %s." % self._errors[0].line_number
1349
1241
            if len(self._errors) > 1:
1350
 
                msg = "Parsing failed with several errors.\nFirst error %s" % info
 
1242
                msg = ("Parsing failed with several errors.\nFirst error %s" %
 
1243
                    info)
1351
1244
                error = ConfigObjError(msg)
1352
1245
            else:
1353
1246
                error = self._errors[0]
1359
1252
            raise error
1360
1253
        # delete private attributes
1361
1254
        del self._errors
1362
 
        
1363
 
        if configspec is None:
 
1255
        #
 
1256
        if defaults['configspec'] is None:
1364
1257
            self.configspec = None
1365
1258
        else:
1366
 
            self._handle_configspec(configspec)
1367
 
    
1368
 
    
1369
 
    def _initialise(self, options=None):
1370
 
        if options is None:
1371
 
            options = OPTION_DEFAULTS
1372
 
            
1373
 
        # initialise a few variables
1374
 
        self.filename = None
1375
 
        self._errors = []
1376
 
        self.raise_errors = options['raise_errors']
1377
 
        self.interpolation = options['interpolation']
1378
 
        self.list_values = options['list_values']
1379
 
        self.create_empty = options['create_empty']
1380
 
        self.file_error = options['file_error']
1381
 
        self.stringify = options['stringify']
1382
 
        self.indent_type = options['indent_type']
1383
 
        self.encoding = options['encoding']
1384
 
        self.default_encoding = options['default_encoding']
1385
 
        self.BOM = False
1386
 
        self.newlines = None
1387
 
        self.write_empty_values = options['write_empty_values']
1388
 
        self.unrepr = options['unrepr']
1389
 
        
1390
 
        self.initial_comment = []
1391
 
        self.final_comment = []
1392
 
        self.configspec = {}
1393
 
        
1394
 
        # Clear section attributes as well
1395
 
        Section._initialise(self)
1396
 
        
1397
 
        
 
1259
            self._handle_configspec(defaults['configspec'])
 
1260
    
1398
1261
    def __repr__(self):
1399
 
        return ('ConfigObj({%s})' % 
1400
 
                ', '.join([('%s: %s' % (repr(key), repr(self[key]))) 
1401
 
                for key in (self.scalars + self.sections)]))
1402
 
    
 
1262
        return 'ConfigObj({%s})' % ', '.join(
 
1263
            [('%s: %s' % (repr(key), repr(self[key]))) for key in
 
1264
            (self.scalars + self.sections)])
1403
1265
    
1404
1266
    def _handle_bom(self, infile):
1405
1267
        """
1429
1291
            # the encoding specified doesn't have one
1430
1292
            # just decode
1431
1293
            return self._decode(infile, self.encoding)
1432
 
        
 
1294
        #
1433
1295
        if isinstance(infile, (list, tuple)):
1434
1296
            line = infile[0]
1435
1297
        else:
1451
1313
                        ##self.BOM = True
1452
1314
                        # Don't need to remove BOM
1453
1315
                        return self._decode(infile, encoding)
1454
 
                    
 
1316
                #
1455
1317
                # If we get this far, will *probably* raise a DecodeError
1456
1318
                # As it doesn't appear to start with a BOM
1457
1319
                return self._decode(infile, self.encoding)
1458
 
            
 
1320
            #
1459
1321
            # Must be UTF8
1460
1322
            BOM = BOM_SET[enc]
1461
1323
            if not line.startswith(BOM):
1462
1324
                return self._decode(infile, self.encoding)
1463
 
            
 
1325
            #
1464
1326
            newline = line[len(BOM):]
1465
 
            
 
1327
            #
1466
1328
            # BOM removed
1467
1329
            if isinstance(infile, (list, tuple)):
1468
1330
                infile[0] = newline
1470
1332
                infile = newline
1471
1333
            self.BOM = True
1472
1334
            return self._decode(infile, self.encoding)
1473
 
        
 
1335
        #
1474
1336
        # No encoding specified - so we need to check for UTF8/UTF16
1475
1337
        for BOM, (encoding, final_encoding) in BOMS.items():
1476
1338
            if not line.startswith(BOM):
1494
1356
                        return infile
1495
1357
                # UTF16 - have to decode
1496
1358
                return self._decode(infile, encoding)
1497
 
            
 
1359
        #
1498
1360
        # No BOM discovered and no encoding specified, just return
1499
1361
        if isinstance(infile, StringTypes):
1500
1362
            # infile read from a file will be a single string
1501
1363
            return infile.splitlines(True)
1502
 
        return infile
1503
 
 
 
1364
        else:
 
1365
            return infile
1504
1366
 
1505
1367
    def _a_to_u(self, aString):
1506
1368
        """Decode ASCII strings to unicode if a self.encoding is specified."""
1509
1371
        else:
1510
1372
            return aString
1511
1373
 
1512
 
 
1513
1374
    def _decode(self, infile, encoding):
1514
1375
        """
1515
1376
        Decode infile to unicode. Using the specified encoding.
1528
1389
                infile[i] = line.decode(encoding)
1529
1390
        return infile
1530
1391
 
1531
 
 
1532
1392
    def _decode_element(self, line):
1533
1393
        """Decode element to unicode if necessary."""
1534
1394
        if not self.encoding:
1537
1397
            return line.decode(self.default_encoding)
1538
1398
        return line
1539
1399
 
1540
 
 
1541
1400
    def _str(self, value):
1542
1401
        """
1543
1402
        Used by ``stringify`` within validate, to turn non-string values
1548
1407
        else:
1549
1408
            return value
1550
1409
 
1551
 
 
1552
1410
    def _parse(self, infile):
1553
1411
        """Actually parse the config file."""
1554
1412
        temp_list_values = self.list_values
1555
1413
        if self.unrepr:
1556
1414
            self.list_values = False
1557
 
            
1558
1415
        comment_list = []
1559
1416
        done_start = False
1560
1417
        this_section = self
1561
1418
        maxline = len(infile) - 1
1562
1419
        cur_index = -1
1563
1420
        reset_comment = False
1564
 
        
1565
1421
        while cur_index < maxline:
1566
1422
            if reset_comment:
1567
1423
                comment_list = []
1573
1429
                reset_comment = False
1574
1430
                comment_list.append(line)
1575
1431
                continue
1576
 
            
1577
1432
            if not done_start:
1578
1433
                # preserve initial comment
1579
1434
                self.initial_comment = comment_list
1580
1435
                comment_list = []
1581
1436
                done_start = True
1582
 
                
1583
1437
            reset_comment = True
1584
1438
            # first we check if it's a section marker
1585
1439
            mat = self._sectionmarker.match(line)
1586
1440
            if mat is not None:
1587
1441
                # is a section line
1588
 
                (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
 
1442
                (indent, sect_open, sect_name, sect_close, comment) = (
 
1443
                    mat.groups())
1589
1444
                if indent and (self.indent_type is None):
1590
1445
                    self.indent_type = indent
1591
1446
                cur_depth = sect_open.count('[')
1592
1447
                if cur_depth != sect_close.count(']'):
1593
 
                    self._handle_error("Cannot compute the section depth at line %s.",
1594
 
                                       NestingError, infile, cur_index)
 
1448
                    self._handle_error(
 
1449
                        "Cannot compute the section depth at line %s.",
 
1450
                        NestingError, infile, cur_index)
1595
1451
                    continue
1596
 
                
 
1452
                #
1597
1453
                if cur_depth < this_section.depth:
1598
1454
                    # the new section is dropping back to a previous level
1599
1455
                    try:
1600
 
                        parent = self._match_depth(this_section,
1601
 
                                                   cur_depth).parent
 
1456
                        parent = self._match_depth(
 
1457
                            this_section,
 
1458
                            cur_depth).parent
1602
1459
                    except SyntaxError:
1603
 
                        self._handle_error("Cannot compute nesting level at line %s.",
1604
 
                                           NestingError, infile, cur_index)
 
1460
                        self._handle_error(
 
1461
                            "Cannot compute nesting level at line %s.",
 
1462
                            NestingError, infile, cur_index)
1605
1463
                        continue
1606
1464
                elif cur_depth == this_section.depth:
1607
1465
                    # the new section is a sibling of the current section
1610
1468
                    # the new section is a child the current section
1611
1469
                    parent = this_section
1612
1470
                else:
1613
 
                    self._handle_error("Section too nested at line %s.",
1614
 
                                       NestingError, infile, cur_index)
1615
 
                    
 
1471
                    self._handle_error(
 
1472
                        "Section too nested at line %s.",
 
1473
                        NestingError, infile, cur_index)
 
1474
                #
1616
1475
                sect_name = self._unquote(sect_name)
1617
1476
                if parent.has_key(sect_name):
1618
 
                    self._handle_error('Duplicate section name at line %s.',
1619
 
                                       DuplicateError, infile, cur_index)
 
1477
                    self._handle_error(
 
1478
                        'Duplicate section name at line %s.',
 
1479
                        DuplicateError, infile, cur_index)
1620
1480
                    continue
1621
 
                
1622
1481
                # create the new section
1623
1482
                this_section = Section(
1624
1483
                    parent,
1708
1567
        if self.indent_type is None:
1709
1568
            # no indentation used, set the type accordingly
1710
1569
            self.indent_type = ''
1711
 
 
 
1570
        #
 
1571
        if self._terminated:
 
1572
            comment_list.append('')
1712
1573
        # preserve the final comment
1713
1574
        if not self and not self.initial_comment:
1714
1575
            self.initial_comment = comment_list
1716
1577
            self.final_comment = comment_list
1717
1578
        self.list_values = temp_list_values
1718
1579
 
1719
 
 
1720
1580
    def _match_depth(self, sect, depth):
1721
1581
        """
1722
1582
        Given a section and a depth level, walk back through the sections
1728
1588
        while depth < sect.depth:
1729
1589
            if sect is sect.parent:
1730
1590
                # we've reached the top level already
1731
 
                raise SyntaxError()
 
1591
                raise SyntaxError
1732
1592
            sect = sect.parent
1733
1593
        if sect.depth == depth:
1734
1594
            return sect
1735
1595
        # shouldn't get here
1736
 
        raise SyntaxError()
1737
 
 
 
1596
        raise SyntaxError
1738
1597
 
1739
1598
    def _handle_error(self, text, ErrorClass, infile, cur_index):
1740
1599
        """
1754
1613
        # reraise when parsing has finished
1755
1614
        self._errors.append(error)
1756
1615
 
1757
 
 
1758
1616
    def _unquote(self, value):
1759
1617
        """Return an unquoted version of a value"""
1760
1618
        if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1761
1619
            value = value[1:-1]
1762
1620
        return value
1763
1621
 
1764
 
 
1765
1622
    def _quote(self, value, multiline=True):
1766
1623
        """
1767
1624
        Return a safely quoted version of a value.
1776
1633
        Obey list syntax for empty and single member lists.
1777
1634
        
1778
1635
        If ``list_values=False`` then the value is only quoted if it contains
1779
 
        a ``\n`` (is multiline) or '#'.
 
1636
        a ``\n`` (is multiline).
1780
1637
        
1781
1638
        If ``write_empty_values`` is set, and the value is an empty string, it
1782
1639
        won't be quoted.
1785
1642
            # Only if multiline is set, so that it is used for values not
1786
1643
            # keys, and not values that are part of a list
1787
1644
            return ''
1788
 
        
1789
1645
        if multiline and isinstance(value, (list, tuple)):
1790
1646
            if not value:
1791
1647
                return ','
1797
1653
            if self.stringify:
1798
1654
                value = str(value)
1799
1655
            else:
1800
 
                raise TypeError('Value "%s" is not a string.' % value)
1801
 
 
 
1656
                raise TypeError, 'Value "%s" is not a string.' % value
 
1657
        squot = "'%s'"
 
1658
        dquot = '"%s"'
 
1659
        noquot = "%s"
 
1660
        wspace_plus = ' \r\t\n\v\t\'"'
 
1661
        tsquot = '"""%s"""'
 
1662
        tdquot = "'''%s'''"
1802
1663
        if not value:
1803
1664
            return '""'
1804
 
        
1805
 
        no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1806
 
        need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1807
 
        hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1808
 
        check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1809
 
        
1810
 
        if check_for_single:
 
1665
        if (not self.list_values and '\n' not in value) or not (multiline and
 
1666
                ((("'" in value) and ('"' in value)) or ('\n' in value))):
1811
1667
            if not self.list_values:
1812
1668
                # we don't quote if ``list_values=False``
1813
1669
                quot = noquot
1814
1670
            # for normal values either single or double quotes will do
1815
1671
            elif '\n' in value:
1816
1672
                # will only happen if multiline is off - e.g. '\n' in key
1817
 
                raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
 
1673
                raise ConfigObjError, ('Value "%s" cannot be safely quoted.' %
 
1674
                    value)
1818
1675
            elif ((value[0] not in wspace_plus) and
1819
1676
                    (value[-1] not in wspace_plus) and
1820
1677
                    (',' not in value)):
1821
1678
                quot = noquot
1822
1679
            else:
1823
 
                quot = self._get_single_quote(value)
 
1680
                if ("'" in value) and ('"' in value):
 
1681
                    raise ConfigObjError, (
 
1682
                        'Value "%s" cannot be safely quoted.' % value)
 
1683
                elif '"' in value:
 
1684
                    quot = squot
 
1685
                else:
 
1686
                    quot = dquot
1824
1687
        else:
1825
1688
            # if value has '\n' or "'" *and* '"', it will need triple quotes
1826
 
            quot = self._get_triple_quote(value)
1827
 
        
1828
 
        if quot == noquot and '#' in value and self.list_values:
1829
 
            quot = self._get_single_quote(value)
1830
 
                
 
1689
            if (value.find('"""') != -1) and (value.find("'''") != -1):
 
1690
                raise ConfigObjError, (
 
1691
                    'Value "%s" cannot be safely quoted.' % value)
 
1692
            if value.find('"""') == -1:
 
1693
                quot = tdquot
 
1694
            else:
 
1695
                quot = tsquot
1831
1696
        return quot % value
1832
 
    
1833
 
    
1834
 
    def _get_single_quote(self, value):
1835
 
        if ("'" in value) and ('"' in value):
1836
 
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1837
 
        elif '"' in value:
1838
 
            quot = squot
1839
 
        else:
1840
 
            quot = dquot
1841
 
        return quot
1842
 
    
1843
 
    
1844
 
    def _get_triple_quote(self, value):
1845
 
        if (value.find('"""') != -1) and (value.find("'''") != -1):
1846
 
            raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1847
 
        if value.find('"""') == -1:
1848
 
            quot = tdquot
1849
 
        else:
1850
 
            quot = tsquot 
1851
 
        return quot
1852
 
 
1853
1697
 
1854
1698
    def _handle_value(self, value):
1855
1699
        """
1860
1704
        if not self.list_values:
1861
1705
            mat = self._nolistvalue.match(value)
1862
1706
            if mat is None:
1863
 
                raise SyntaxError()
 
1707
                raise SyntaxError
1864
1708
            # NOTE: we don't unquote here
1865
1709
            return mat.groups()
1866
1710
        #
1868
1712
        if mat is None:
1869
1713
            # the value is badly constructed, probably badly quoted,
1870
1714
            # or an invalid list
1871
 
            raise SyntaxError()
 
1715
            raise SyntaxError
1872
1716
        (list_values, single, empty_list, comment) = mat.groups()
1873
1717
        if (list_values == '') and (single is None):
1874
1718
            # change this if you want to accept empty values
1875
 
            raise SyntaxError()
 
1719
            raise SyntaxError
1876
1720
        # NOTE: note there is no error handling from here if the regex
1877
1721
        # is wrong: then incorrect values will slip through
1878
1722
        if empty_list is not None:
1896
1740
            the_list += [single]
1897
1741
        return (the_list, comment)
1898
1742
 
1899
 
 
1900
1743
    def _multiline(self, value, infile, cur_index, maxline):
1901
1744
        """Extract the value, where we are in a multiline situation."""
1902
1745
        quot = value[:3]
1910
1753
            return retval
1911
1754
        elif newvalue.find(quot) != -1:
1912
1755
            # somehow the triple quote is missing
1913
 
            raise SyntaxError()
 
1756
            raise SyntaxError
1914
1757
        #
1915
1758
        while cur_index < maxline:
1916
1759
            cur_index += 1
1923
1766
                break
1924
1767
        else:
1925
1768
            # we've got to the end of the config, oops...
1926
 
            raise SyntaxError()
 
1769
            raise SyntaxError
1927
1770
        mat = multi_line.match(line)
1928
1771
        if mat is None:
1929
1772
            # a badly formed line
1930
 
            raise SyntaxError()
 
1773
            raise SyntaxError
1931
1774
        (value, comment) = mat.groups()
1932
1775
        return (newvalue + value, comment, cur_index)
1933
1776
 
1934
 
 
1935
1777
    def _handle_configspec(self, configspec):
1936
1778
        """Parse the configspec."""
1937
1779
        # FIXME: Should we check that the configspec was created with the 
1938
 
        #        correct settings ? (i.e. ``list_values=False``)
 
1780
        #   correct settings ? (i.e. ``list_values=False``)
1939
1781
        if not isinstance(configspec, ConfigObj):
1940
1782
            try:
1941
 
                configspec = ConfigObj(configspec,
1942
 
                                       raise_errors=True,
1943
 
                                       file_error=True,
1944
 
                                       list_values=False)
 
1783
                configspec = ConfigObj(
 
1784
                    configspec,
 
1785
                    raise_errors=True,
 
1786
                    file_error=True,
 
1787
                    list_values=False)
1945
1788
            except ConfigObjError, e:
1946
1789
                # FIXME: Should these errors have a reference
1947
 
                #        to the already parsed ConfigObj ?
 
1790
                # to the already parsed ConfigObj ?
1948
1791
                raise ConfigspecError('Parsing configspec failed: %s' % e)
1949
1792
            except IOError, e:
1950
1793
                raise IOError('Reading configspec failed: %s' % e)
1951
 
        
1952
1794
        self._set_configspec_value(configspec, self)
1953
1795
 
1954
 
 
1955
1796
    def _set_configspec_value(self, configspec, section):
1956
1797
        """Used to recursively set configspec values."""
1957
1798
        if '__many__' in configspec.sections:
1958
1799
            section.configspec['__many__'] = configspec['__many__']
1959
1800
            if len(configspec.sections) > 1:
1960
1801
                # FIXME: can we supply any useful information here ?
1961
 
                raise RepeatSectionError()
1962
 
            
1963
 
        if getattr(configspec, 'initial_comment', None) is not None:
 
1802
                raise RepeatSectionError
 
1803
        if hasattr(configspec, 'initial_comment'):
1964
1804
            section._configspec_initial_comment = configspec.initial_comment
1965
1805
            section._configspec_final_comment = configspec.final_comment
1966
1806
            section._configspec_encoding = configspec.encoding
1967
1807
            section._configspec_BOM = configspec.BOM
1968
1808
            section._configspec_newlines = configspec.newlines
1969
1809
            section._configspec_indent_type = configspec.indent_type
1970
 
            
1971
1810
        for entry in configspec.scalars:
1972
1811
            section._configspec_comments[entry] = configspec.comments[entry]
1973
 
            section._configspec_inline_comments[entry] = configspec.inline_comments[entry]
 
1812
            section._configspec_inline_comments[entry] = (
 
1813
                configspec.inline_comments[entry])
1974
1814
            section.configspec[entry] = configspec[entry]
1975
1815
            section._order.append(entry)
1976
 
            
1977
1816
        for entry in configspec.sections:
1978
1817
            if entry == '__many__':
1979
1818
                continue
1980
 
            
1981
1819
            section._cs_section_comments[entry] = configspec.comments[entry]
1982
 
            section._cs_section_inline_comments[entry] = configspec.inline_comments[entry]
 
1820
            section._cs_section_inline_comments[entry] = (
 
1821
                configspec.inline_comments[entry])
1983
1822
            if not section.has_key(entry):
1984
1823
                section[entry] = {}
1985
1824
            self._set_configspec_value(configspec[entry], section[entry])
1986
1825
 
1987
 
 
1988
1826
    def _handle_repeat(self, section, configspec):
1989
1827
        """Dynamically assign configspec for repeated section."""
1990
1828
        try:
1995
1833
                                if isinstance(configspec[entry], dict)]
1996
1834
            scalar_keys = [entry for entry in configspec 
1997
1835
                                if not isinstance(configspec[entry], dict)]
1998
 
            
1999
1836
        if '__many__' in section_keys and len(section_keys) > 1:
2000
1837
            # FIXME: can we supply any useful information here ?
2001
 
            raise RepeatSectionError()
2002
 
        
 
1838
            raise RepeatSectionError
2003
1839
        scalars = {}
2004
1840
        sections = {}
2005
1841
        for entry in scalar_keys:
2011
1847
                scalars[entry] = val
2012
1848
                continue
2013
1849
            sections[entry] = val
2014
 
            
 
1850
        #
2015
1851
        section.configspec = scalars
2016
1852
        for entry in sections:
2017
1853
            if not section.has_key(entry):
2018
1854
                section[entry] = {}
2019
1855
            self._handle_repeat(section[entry], sections[entry])
2020
1856
 
2021
 
 
2022
1857
    def _write_line(self, indent_string, entry, this_entry, comment):
2023
1858
        """Write an individual line, for the write method"""
2024
1859
        # NOTE: the calls to self._quote here handles non-StringType values.
2026
1861
            val = self._decode_element(self._quote(this_entry))
2027
1862
        else:
2028
1863
            val = repr(this_entry)
2029
 
        return '%s%s%s%s%s' % (indent_string,
2030
 
                               self._decode_element(self._quote(entry, multiline=False)),
2031
 
                               self._a_to_u(' = '),
2032
 
                               val,
2033
 
                               self._decode_element(comment))
2034
 
 
 
1864
        return '%s%s%s%s%s' % (
 
1865
            indent_string,
 
1866
            self._decode_element(self._quote(entry, multiline=False)),
 
1867
            self._a_to_u(' = '),
 
1868
            val,
 
1869
            self._decode_element(comment))
2035
1870
 
2036
1871
    def _write_marker(self, indent_string, depth, entry, comment):
2037
1872
        """Write a section marker line"""
2038
 
        return '%s%s%s%s%s' % (indent_string,
2039
 
                               self._a_to_u('[' * depth),
2040
 
                               self._quote(self._decode_element(entry), multiline=False),
2041
 
                               self._a_to_u(']' * depth),
2042
 
                               self._decode_element(comment))
2043
 
 
 
1873
        return '%s%s%s%s%s' % (
 
1874
            indent_string,
 
1875
            self._a_to_u('[' * depth),
 
1876
            self._quote(self._decode_element(entry), multiline=False),
 
1877
            self._a_to_u(']' * depth),
 
1878
            self._decode_element(comment))
2044
1879
 
2045
1880
    def _handle_comment(self, comment):
2046
1881
        """Deal with a comment."""
2051
1886
            start += self._a_to_u(' # ')
2052
1887
        return (start + comment)
2053
1888
 
2054
 
 
2055
1889
    # Public methods
2056
1890
 
2057
1891
    def write(self, outfile=None, section=None):
2070
1904
        if self.indent_type is None:
2071
1905
            # this can be true if initialised from a dictionary
2072
1906
            self.indent_type = DEFAULT_INDENT_TYPE
2073
 
            
 
1907
        #
2074
1908
        out = []
2075
1909
        cs = self._a_to_u('#')
2076
1910
        csp = self._a_to_u('# ')
2084
1918
                if stripped_line and not stripped_line.startswith(cs):
2085
1919
                    line = csp + line
2086
1920
                out.append(line)
2087
 
                
 
1921
        #
2088
1922
        indent_string = self.indent_type * section.depth
2089
1923
        for entry in (section.scalars + section.sections):
2090
1924
            if entry in section.defaults:
2097
1931
                out.append(indent_string + comment_line)
2098
1932
            this_entry = section[entry]
2099
1933
            comment = self._handle_comment(section.inline_comments[entry])
2100
 
            
 
1934
            #
2101
1935
            if isinstance(this_entry, dict):
2102
1936
                # a section
2103
1937
                out.append(self._write_marker(
2112
1946
                    entry,
2113
1947
                    this_entry,
2114
1948
                    comment))
2115
 
                
 
1949
        #
2116
1950
        if section is self:
2117
1951
            for line in self.final_comment:
2118
1952
                line = self._decode_element(line)
2121
1955
                    line = csp + line
2122
1956
                out.append(line)
2123
1957
            self.interpolation = int_val
2124
 
            
 
1958
        #
2125
1959
        if section is not self:
2126
1960
            return out
2127
 
        
 
1961
        #
2128
1962
        if (self.filename is None) and (outfile is None):
2129
1963
            # output a list of lines
2130
1964
            # might need to encode
2138
1972
                    out.append('')
2139
1973
                out[0] = BOM_UTF8 + out[0]
2140
1974
            return out
2141
 
        
 
1975
        #
2142
1976
        # Turn the list to a string, joined with correct newlines
2143
 
        newline = self.newlines or os.linesep
2144
 
        output = self._a_to_u(newline).join(out)
 
1977
        output = (self._a_to_u(self.newlines or os.linesep)
 
1978
            ).join(out)
2145
1979
        if self.encoding:
2146
1980
            output = output.encode(self.encoding)
2147
 
        if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
 
1981
        if (self.BOM and ((self.encoding is None) or
 
1982
            (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2148
1983
            # Add the UTF8 BOM
2149
1984
            output = BOM_UTF8 + output
2150
 
            
2151
 
        if not output.endswith(newline):
2152
 
            output += newline
2153
1985
        if outfile is not None:
2154
1986
            outfile.write(output)
2155
1987
        else:
2157
1989
            h.write(output)
2158
1990
            h.close()
2159
1991
 
2160
 
 
2161
1992
    def validate(self, validator, preserve_errors=False, copy=False,
2162
 
                 section=None):
 
1993
        section=None):
2163
1994
        """
2164
1995
        Test the ConfigObj against a configspec.
2165
1996
        
2184
2015
        If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2185
2016
        of a marking a fail with a ``False``, it will preserve the actual
2186
2017
        exception object. This can contain info about the reason for failure.
2187
 
        For example the ``VdtValueTooSmallError`` indicates that the value
 
2018
        For example the ``VdtValueTooSmallError`` indeicates that the value
2188
2019
        supplied was too small. If a value (or section) is missing it will
2189
2020
        still be marked as ``False``.
2190
2021
        
2196
2027
        """
2197
2028
        if section is None:
2198
2029
            if self.configspec is None:
2199
 
                raise ValueError('No configspec supplied.')
 
2030
                raise ValueError, 'No configspec supplied.'
2200
2031
            if preserve_errors:
2201
 
                # We do this once to remove a top level dependency on the validate module
2202
 
                # Which makes importing configobj faster
2203
 
                from validate import VdtMissingValue
2204
 
                self._vdtMissingValue = VdtMissingValue
 
2032
                if VdtMissingValue is None:
 
2033
                    raise ImportError('Missing validate module.')
2205
2034
            section = self
2206
2035
        #
2207
2036
        spec_section = section.configspec
2208
 
        if copy and getattr(section, '_configspec_initial_comment', None) is not None:
 
2037
        if copy and hasattr(section, '_configspec_initial_comment'):
2209
2038
            section.initial_comment = section._configspec_initial_comment
2210
2039
            section.final_comment = section._configspec_final_comment
2211
2040
            section.encoding = section._configspec_encoding
2212
2041
            section.BOM = section._configspec_BOM
2213
2042
            section.newlines = section._configspec_newlines
2214
2043
            section.indent_type = section._configspec_indent_type
2215
 
            
2216
2044
        if '__many__' in section.configspec:
2217
2045
            many = spec_section['__many__']
2218
2046
            # dynamically assign the configspecs
2249
2077
                                        missing=missing
2250
2078
                                        )
2251
2079
            except validator.baseErrorClass, e:
2252
 
                if not preserve_errors or isinstance(e, self._vdtMissingValue):
 
2080
                if not preserve_errors or isinstance(e, VdtMissingValue):
2253
2081
                    out[entry] = False
2254
2082
                else:
2255
2083
                    # preserve the error
2257
2085
                    ret_false = False
2258
2086
                ret_true = False
2259
2087
            else:
2260
 
                try: 
2261
 
                    section.default_values.pop(entry, None)
2262
 
                except AttributeError: 
2263
 
                    # For Python 2.2 compatibility
2264
 
                    try:
2265
 
                        del section.default_values[entry]
2266
 
                    except KeyError:
2267
 
                        pass
2268
 
                    
2269
 
                if getattr(validator, 'get_default_value', None) is not None:
2270
 
                    try: 
2271
 
                        section.default_values[entry] = validator.get_default_value(spec_section[entry])
2272
 
                    except KeyError:
2273
 
                        # No default
2274
 
                        pass
2275
 
                    
2276
2088
                ret_false = False
2277
2089
                out[entry] = True
2278
2090
                if self.stringify or missing:
2291
2103
                        section[entry] = check
2292
2104
                if not copy and missing and entry not in section.defaults:
2293
2105
                    section.defaults.append(entry)
 
2106
        #
2294
2107
        # Missing sections will have been created as empty ones when the
2295
2108
        # configspec was read.
2296
2109
        for entry in section.sections:
2316
2129
            return True
2317
2130
        elif ret_false:
2318
2131
            return False
2319
 
        return out
2320
 
 
2321
 
 
2322
 
    def reset(self):
2323
 
        """Clear ConfigObj instance and restore to 'freshly created' state."""
2324
 
        self.clear()
2325
 
        self._initialise()
2326
 
        # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2327
 
        #        requires an empty dictionary
2328
 
        self.configspec = None
2329
 
        # Just to be sure ;-)
2330
 
        self._original_configspec = None
2331
 
        
2332
 
        
2333
 
    def reload(self):
2334
 
        """
2335
 
        Reload a ConfigObj from file.
2336
 
        
2337
 
        This method raises a ``ReloadError`` if the ConfigObj doesn't have
2338
 
        a filename attribute pointing to a file.
2339
 
        """
2340
 
        if not isinstance(self.filename, StringTypes):
2341
 
            raise ReloadError()
2342
 
 
2343
 
        filename = self.filename
2344
 
        current_options = {}
2345
 
        for entry in OPTION_DEFAULTS:
2346
 
            if entry == 'configspec':
2347
 
                continue
2348
 
            current_options[entry] = getattr(self, entry)
2349
 
            
2350
 
        configspec = self._original_configspec
2351
 
        current_options['configspec'] = configspec
2352
 
            
2353
 
        self.clear()
2354
 
        self._initialise(current_options)
2355
 
        self._load(filename, configspec)
2356
 
        
2357
 
 
 
2132
        else:
 
2133
            return out
2358
2134
 
2359
2135
class SimpleVal(object):
2360
2136
    """
2374
2150
    def check(self, check, member, missing=False):
2375
2151
        """A dummy check method, always returns the value unchanged."""
2376
2152
        if missing:
2377
 
            raise self.baseErrorClass()
 
2153
            raise self.baseErrorClass
2378
2154
        return member
2379
2155
 
2380
 
 
2381
2156
# Check / processing functions for options
2382
2157
def flatten_errors(cfg, res, levels=None, results=None):
2383
2158
    """
2501
2276
    #
2502
2277
    return results
2503
2278
 
2504
 
 
2505
2279
"""*A programming language is a medium of expression.* - Paul Graham"""