~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

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

  • Committer: Andrew Bennetts
  • Date: 2008-03-27 06:10:18 UTC
  • mfrom: (3309 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3320.
  • Revision ID: andrew.bennetts@canonical.com-20080327061018-dxztpxyv6yoeg3am
Merge from bzr.dev.

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