81
159
'stringify': True,
82
160
# option may be set to one of ('', ' ', '\t')
83
161
'indent_type': None,
163
'default_encoding': None,
165
'write_empty_values': False,
173
raise ImportError('compiler module not available')
174
p = compiler.parse(s)
175
return p.getChildren()[1].getChildren()[0].getChildren()[1]
178
class UnknownType(Exception):
182
class Builder(object):
185
m = getattr(self, 'build_' + o.__class__.__name__, None)
187
raise UnknownType(o.__class__.__name__)
190
def build_List(self, o):
191
return map(self.build, o.getChildren())
193
def build_Const(self, o):
196
def build_Dict(self, o):
198
i = iter(map(self.build, o.getChildren()))
203
def build_Tuple(self, o):
204
return tuple(self.build_List(o))
206
def build_Name(self, o):
211
if o.name == 'False':
215
raise UnknownType('Undefined Name')
217
def build_Add(self, o):
218
real, imag = map(self.build_Const, o.getChildren())
222
raise UnknownType('Add')
223
if not isinstance(imag, complex) or imag.real != 0.0:
224
raise UnknownType('Add')
227
def build_Getattr(self, o):
228
parent = self.build(o.expr)
229
return getattr(parent, o.attrname)
231
def build_UnarySub(self, o):
232
return -self.build_Const(o.getChildren()[0])
234
def build_UnaryAdd(self, o):
235
return self.build_Const(o.getChildren()[0])
244
return _builder.build(getObj(s))
86
248
class ConfigObjError(SyntaxError):
88
250
This is the base class for all errors that ConfigObj raises.
89
251
It is a subclass of SyntaxError.
91
>>> raise ConfigObjError
92
Traceback (most recent call last):
95
253
def __init__(self, message='', line_number=None, line=''):
97
255
self.line_number = line_number
98
self.message = message
99
256
SyntaxError.__init__(self, message)
101
259
class NestingError(ConfigObjError):
103
261
This error indicates a level of nesting that doesn't match.
105
>>> raise NestingError
106
Traceback (most recent call last):
110
265
class ParseError(ConfigObjError):
112
267
This error indicates that a line is badly written.
113
268
It is neither a valid ``key = value`` line,
114
269
nor a valid section marker line.
117
Traceback (most recent call last):
273
class ReloadError(IOError):
275
A 'reload' operation failed.
276
This exception is a subclass of ``IOError``.
279
IOError.__init__(self, 'reload failed, filename is not set.')
121
282
class DuplicateError(ConfigObjError):
123
284
The keyword or section specified already exists.
125
>>> raise DuplicateError
126
Traceback (most recent call last):
130
288
class ConfigspecError(ConfigObjError):
132
290
An error occured whilst parsing a configspec.
134
>>> raise ConfigspecError
135
Traceback (most recent call last):
139
294
class InterpolationError(ConfigObjError):
140
295
"""Base class for the two interpolation errors."""
142
class InterpolationDepthError(InterpolationError):
298
class InterpolationLoopError(InterpolationError):
143
299
"""Maximum interpolation depth exceeded in string interpolation."""
145
301
def __init__(self, option):
147
>>> raise InterpolationDepthError('yoda')
148
Traceback (most recent call last):
149
InterpolationDepthError: max interpolation depth exceeded in value "yoda".
151
302
InterpolationError.__init__(
153
'max interpolation depth exceeded in value "%s".' % option)
304
'interpolation loop detected in value "%s".' % option)
155
307
class RepeatSectionError(ConfigObjError):
157
309
This error indicates additional sections in a section with a
158
310
``__many__`` (repeated) section.
160
>>> raise RepeatSectionError
161
Traceback (most recent call last):
165
314
class MissingInterpolationOption(InterpolationError):
166
315
"""A value specified for interpolation was missing."""
168
317
def __init__(self, option):
170
>>> raise MissingInterpolationOption('yoda')
171
Traceback (most recent call last):
172
MissingInterpolationOption: missing option "yoda" in interpolation.
174
318
InterpolationError.__init__(
176
320
'missing option "%s" in interpolation.' % option)
323
class UnreprError(ConfigObjError):
324
"""An error parsing in unrepr mode."""
328
class InterpolationEngine(object):
330
A helper class to help perform string interpolation.
332
This class is an abstract base class; its descendants perform
336
# compiled regexp to use in self.interpolate()
337
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
339
def __init__(self, section):
340
# the Section instance that "owns" this engine
341
self.section = section
344
def interpolate(self, key, value):
345
def recursive_interpolate(key, value, section, backtrail):
346
"""The function that does the actual work.
348
``value``: the string we're trying to interpolate.
349
``section``: the section in which that string was found
350
``backtrail``: a dict to keep track of where we've been,
351
to detect and prevent infinite recursion loops
353
This is similar to a depth-first-search algorithm.
355
# Have we been here already?
356
if (key, section.name) in backtrail:
357
# Yes - infinite loop detected
358
raise InterpolationLoopError(key)
359
# Place a marker on our backtrail so we won't come back here again
360
backtrail[(key, section.name)] = 1
362
# Now start the actual work
363
match = self._KEYCRE.search(value)
365
# The actual parsing of the match is implementation-dependent,
366
# so delegate to our helper function
367
k, v, s = self._parse_match(match)
369
# That's the signal that no further interpolation is needed
372
# Further interpolation may be needed to obtain final value
373
replacement = recursive_interpolate(k, v, s, backtrail)
374
# Replace the matched string with its final value
375
start, end = match.span()
376
value = ''.join((value[:start], replacement, value[end:]))
377
new_search_start = start + len(replacement)
378
# Pick up the next interpolation key, if any, for next time
379
# through the while loop
380
match = self._KEYCRE.search(value, new_search_start)
382
# Now safe to come back here again; remove marker from backtrail
383
del backtrail[(key, section.name)]
387
# Back in interpolate(), all we have to do is kick off the recursive
388
# function with appropriate starting values
389
value = recursive_interpolate(key, value, self.section, {})
393
def _fetch(self, key):
394
"""Helper function to fetch values from owning section.
396
Returns a 2-tuple: the value, and the section where it was found.
398
# switch off interpolation before we try and fetch anything !
399
save_interp = self.section.main.interpolation
400
self.section.main.interpolation = False
402
# Start at section that "owns" this InterpolationEngine
403
current_section = self.section
405
# try the current section first
406
val = current_section.get(key)
410
val = current_section.get('DEFAULT', {}).get(key)
413
# move up to parent and try again
414
# top-level's parent is itself
415
if current_section.parent is current_section:
416
# reached top level, time to give up
418
current_section = current_section.parent
420
# restore interpolation to previous value before returning
421
self.section.main.interpolation = save_interp
423
raise MissingInterpolationOption(key)
424
return val, current_section
427
def _parse_match(self, match):
428
"""Implementation-dependent helper function.
430
Will be passed a match object corresponding to the interpolation
431
key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
432
key in the appropriate config file section (using the ``_fetch()``
433
helper function) and return a 3-tuple: (key, value, section)
435
``key`` is the name of the key we're looking for
436
``value`` is the value found for that key
437
``section`` is a reference to the section where it was found
439
``key`` and ``section`` should be None if no further
440
interpolation should be performed on the resulting value
441
(e.g., if we interpolated "$$" and returned "$").
443
raise NotImplementedError()
447
class ConfigParserInterpolation(InterpolationEngine):
448
"""Behaves like ConfigParser."""
449
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
451
def _parse_match(self, match):
453
value, section = self._fetch(key)
454
return key, value, section
458
class TemplateInterpolation(InterpolationEngine):
459
"""Behaves like string.Template."""
461
_KEYCRE = re.compile(r"""
463
(?P<escaped>\$) | # Two $ signs
464
(?P<named>[_a-z][_a-z0-9]*) | # $name format
465
{(?P<braced>[^}]*)} # ${name} format
467
""", re.IGNORECASE | re.VERBOSE)
469
def _parse_match(self, match):
470
# Valid name (in or out of braces): fetch value from section
471
key = match.group('named') or match.group('braced')
473
value, section = self._fetch(key)
474
return key, value, section
475
# Escaped delimiter (e.g., $$): return single delimiter
476
if match.group('escaped') is not None:
477
# Return None for key and section to indicate it's time to stop
478
return None, self._delimiter, None
479
# Anything else: ignore completely, just return it unchanged
480
return None, match.group(), None
483
interpolation_engines = {
484
'configparser': ConfigParserInterpolation,
485
'template': TemplateInterpolation,
489
def __newobj__(cls, *args):
491
return cls.__new__(cls, *args)
178
493
class Section(dict):
180
495
A dictionary-like object that represents a section in a config file.
182
It does string interpolation if the 'interpolate' attribute
497
It does string interpolation if the 'interpolation' attribute
183
498
of the 'main' object is set to True.
185
Interpolation is tried first from the 'DEFAULT' section of this object,
186
next from the 'DEFAULT' section of the parent, lastly the main object.
500
Interpolation is tried first from this object, then from the 'DEFAULT'
501
section of this object, next from the parent and its 'DEFAULT' section,
502
and so on until the main object is reached.
188
504
A Section will behave like an ordered dictionary - following the
189
505
order of the ``scalars`` and ``sections`` attributes.
190
506
You can use this to change the order of members.
192
508
Iteration follows the order: scalars, then sections.
195
_KEYCRE = re.compile(r"%\(([^)]*)\)s|.")
512
def __setstate__(self, state):
513
dict.update(self, state[0])
514
self.__dict__.update(state[1])
516
def __reduce__(self):
517
state = (dict(self), self.__dict__)
518
return (__newobj__, (self.__class__,), state)
197
521
def __init__(self, parent, depth, main, indict=None, name=None):
199
parent is the section above
200
depth is the depth level of this section
201
main is the main ConfigObj
202
indict is a dictionary to initialise the section with
523
* parent is the section above
524
* depth is the depth level of this section
525
* main is the main ConfigObj
526
* indict is a dictionary to initialise the section with
204
528
if indict is None:
211
535
# level of nesting depth of this Section
212
536
self.depth = depth
537
# purely for information
541
# we do this explicitly so that __setitem__ is used properly
542
# (rather than just passing to ``dict.__init__``)
543
for entry, value in indict.iteritems():
547
def _initialise(self):
213
548
# the sequence of scalar values in this Section
214
549
self.scalars = []
215
550
# the sequence of sections in this Section
216
551
self.sections = []
217
# purely for information
219
552
# for comments :-)
220
553
self.comments = {}
221
554
self.inline_comments = {}
556
self.configspec = None
225
558
self.defaults = []
227
# we do this explicitly so that __setitem__ is used properly
228
# (rather than just passing to ``dict.__init__``)
230
self[entry] = indict[entry]
232
def _interpolate(self, value):
233
"""Nicked from ConfigParser."""
234
depth = MAX_INTERPOL_DEPTH
235
# loop through this until it's done
238
if value.find("%(") != -1:
239
value = self._KEYCRE.sub(self._interpolation_replace, value)
559
self.default_values = {}
562
def _interpolate(self, key, value):
564
# do we already have an interpolation engine?
565
engine = self._interpolation_engine
566
except AttributeError:
567
# not yet: first time running _interpolate(), so pick the engine
568
name = self.main.interpolation
569
if name == True: # note that "if name:" would be incorrect here
570
# backwards-compatibility: interpolation=True means use default
571
name = DEFAULT_INTERPOLATION
572
name = name.lower() # so that "Template", "template", etc. all work
573
class_ = interpolation_engines.get(name, None)
575
# invalid value for self.main.interpolation
576
self.main.interpolation = False
243
raise InterpolationDepthError(value)
579
# save reference to engine so we don't have to do this again
580
engine = self._interpolation_engine = class_(self)
581
# let the engine do the actual work
582
return engine.interpolate(key, value)
246
def _interpolation_replace(self, match):
252
# switch off interpolation before we try and fetch anything !
253
self.main.interpolation = False
254
# try the 'DEFAULT' member of *this section* first
255
val = self.get('DEFAULT', {}).get(s)
256
# try the 'DEFAULT' member of the *parent section* next
258
val = self.parent.get('DEFAULT', {}).get(s)
259
# last, try the 'DEFAULT' member of the *main section*
261
val = self.main.get('DEFAULT', {}).get(s)
262
self.main.interpolation = True
264
raise MissingInterpolationOption(s)
267
585
def __getitem__(self, key):
268
586
"""Fetch the item and do string interpolation."""
269
587
val = dict.__getitem__(self, key)
270
if self.main.interpolation and isinstance(val, StringTypes):
271
return self._interpolate(val)
588
if self.main.interpolation and isinstance(val, basestring):
589
return self._interpolate(key, val)
274
def __setitem__(self, key, value):
593
def __setitem__(self, key, value, unrepr=False):
276
595
Correctly set a value.
278
597
Making dictionary values Section instances.
279
598
(We have to special case 'Section' instances - which are also dicts)
281
600
Keys must be strings.
282
601
Values need only be strings (or lists of strings) if
283
602
``main.stringify`` is set.
604
``unrepr`` must be set when setting a value to a dictionary, without
605
creating a new sub-section.
285
if not isinstance(key, StringTypes):
286
raise ValueError, 'The key "%s" is not a string.' % key
287
## if self.depth is None:
607
if not isinstance(key, basestring):
608
raise ValueError('The key "%s" is not a string.' % key)
289
610
# add the comment
290
if not self.comments.has_key(key):
611
if key not in self.comments:
291
612
self.comments[key] = []
292
613
self.inline_comments[key] = ''
293
614
# remove the entry from defaults
539
def decode(self, encoding):
541
Decode all strings and values to unicode, using the specified encoding.
543
Works with subsections and list values.
545
Uses the ``walk`` method.
547
Testing ``encode`` and ``decode``.
549
>>> m.decode('ascii')
550
>>> def testuni(val):
551
... for entry in val:
552
... if not isinstance(entry, unicode):
553
... print >> sys.stderr, type(entry)
554
... raise AssertionError, 'decode failed.'
555
... if isinstance(val[entry], dict):
556
... testuni(val[entry])
557
... elif not isinstance(val[entry], unicode):
558
... raise AssertionError, 'decode failed.'
560
>>> m.encode('ascii')
564
def decode(section, key, encoding=encoding):
567
if isinstance(val, (list, tuple)):
570
newval.append(entry.decode(encoding))
571
elif isinstance(val, dict):
574
newval = val.decode(encoding)
575
newkey = key.decode(encoding)
576
section.rename(key, newkey)
577
section[newkey] = newval
578
# using ``call_on_sections`` allows us to modify section names
579
self.walk(decode, call_on_sections=True)
581
def encode(self, encoding):
583
Encode all strings and values from unicode,
584
using the specified encoding.
586
Works with subsections and list values.
587
Uses the ``walk`` method.
589
def encode(section, key, encoding=encoding):
592
if isinstance(val, (list, tuple)):
595
newval.append(entry.encode(encoding))
596
elif isinstance(val, dict):
599
newval = val.encode(encoding)
600
newkey = key.encode(encoding)
601
section.rename(key, newkey)
602
section[newkey] = newval
603
self.walk(encode, call_on_sections=True)
947
def as_bool(self, key):
949
Accepts a key as input. The corresponding value must be a string or
950
the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
951
retain compatibility with Python 2.2.
953
If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
956
If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
959
``as_bool`` is not case sensitive.
961
Any other input will raise a ``ValueError``.
966
Traceback (most recent call last):
967
ValueError: Value "fish" is neither True nor False
982
if not isinstance(val, basestring):
983
# TODO: Why do we raise a KeyError here?
986
return self.main._bools[val.lower()]
988
raise ValueError('Value "%s" is neither True nor False' % val)
991
def as_int(self, key):
993
A convenience method which coerces the specified value to an integer.
995
If the value is an invalid literal for ``int``, a ``ValueError`` will
1001
Traceback (most recent call last):
1002
ValueError: invalid literal for int() with base 10: 'fish'
1008
Traceback (most recent call last):
1009
ValueError: invalid literal for int() with base 10: '3.2'
1011
return int(self[key])
1014
def as_float(self, key):
1016
A convenience method which coerces the specified value to a float.
1018
If the value is an invalid literal for ``float``, a ``ValueError`` will
1024
Traceback (most recent call last):
1025
ValueError: invalid literal for float(): fish
1033
return float(self[key])
1036
def as_list(self, key):
1038
A convenience method which fetches the specified value, guaranteeing
1053
if isinstance(result, (tuple, list)):
1058
def restore_default(self, key):
1060
Restore (and return) default value for the specified key.
1062
This method will only work for a ConfigObj that was created
1063
with a configspec and has been validated.
1065
If there is no default value for this key, ``KeyError`` is raised.
1067
default = self.default_values[key]
1068
dict.__setitem__(self, key, default)
1069
if key not in self.defaults:
1070
self.defaults.append(key)
1074
def restore_defaults(self):
1076
Recursively restore default values to all members
1079
This method will only work for a ConfigObj that was created
1080
with a configspec and has been validated.
1082
It doesn't delete or modify entries without default values.
1084
for key in self.default_values:
1085
self.restore_default(key)
1087
for section in self.sections:
1088
self[section].restore_defaults()
605
1091
class ConfigObj(Section):
607
An object to read, create, and write config files.
609
Testing with duplicate keys and sections.
619
>>> ConfigObj(c.split('\\n'), raise_errors = True)
620
Traceback (most recent call last):
621
DuplicateError: Duplicate section name at line 5.
629
... 'member1' = value
633
>>> ConfigObj(d.split('\\n'), raise_errors = True)
634
Traceback (most recent call last):
635
DuplicateError: Duplicate keyword name at line 6.
1092
"""An object to read, create, and write config files."""
638
1094
_keyword = re.compile(r'''^ # line start
639
1095
(\s*) # indentation
722
1182
'"""': (_single_line_double, _multi_line_double),
725
def __init__(self, infile=None, options=None, **kwargs):
1185
# Used by the ``istrue`` Section method
1187
'yes': True, 'no': False,
1188
'on': True, 'off': False,
1189
'1': True, '0': False,
1190
'true': True, 'false': False,
1194
def __init__(self, infile=None, options=None, _inspec=False, **kwargs):
727
Parse or create a config file object.
1196
Parse a config file or create a config file object.
729
1198
``ConfigObj(infile=None, options=None, **kwargs)``
1200
self._inspec = _inspec
1201
# init the superclass
1202
Section.__init__(self, self, 0, self)
1204
infile = infile or []
1205
options = dict(options or {})
735
1207
# keyword arguments take precedence over an options dictionary
736
1208
options.update(kwargs)
737
# init the superclass
738
Section.__init__(self, self, 0, self)
1210
options['list_values'] = False
740
1212
defaults = OPTION_DEFAULTS.copy()
741
for entry in options.keys():
742
if entry not in defaults.keys():
743
raise TypeError, 'Unrecognised option "%s".' % entry
744
# TODO: check the values too
745
# add the explicit options to the defaults
1213
# TODO: check the values too.
1214
for entry in options:
1215
if entry not in defaults:
1216
raise TypeError('Unrecognised option "%s".' % entry)
1218
# Add any explicit options to the defaults
746
1219
defaults.update(options)
748
# initialise a few variables
750
self.raise_errors = defaults['raise_errors']
751
self.interpolation = defaults['interpolation']
752
self.list_values = defaults['list_values']
753
self.create_empty = defaults['create_empty']
754
self.file_error = defaults['file_error']
755
self.stringify = defaults['stringify']
756
self.indent_type = defaults['indent_type']
757
# used by the write method
760
self.initial_comment = []
761
self.final_comment = []
763
if isinstance(infile, StringTypes):
764
self.filename = os.path.abspath(infile)
765
if os.path.isfile(self.filename):
766
infile = open(self.filename).readlines()
1220
self._initialise(defaults)
1221
configspec = defaults['configspec']
1222
self._original_configspec = configspec
1223
self._load(infile, configspec)
1226
def _load(self, infile, configspec):
1227
if isinstance(infile, basestring):
1228
self.filename = infile
1229
if os.path.isfile(infile):
1230
h = open(infile, 'rb')
1231
infile = h.read() or []
767
1233
elif self.file_error:
768
1234
# raise an error if the file doesn't exist
769
raise IOError, 'Config file not found: "%s".' % self.filename
1235
raise IOError('Config file not found: "%s".' % self.filename)
771
1237
# file doesn't already exist
772
1238
if self.create_empty:
773
1239
# this is a good test that the filename specified
774
# isn't impossible - like on a non existent device
775
h = open(self.filename)
1240
# isn't impossible - like on a non-existent device
1241
h = open(infile, 'w')
779
1246
elif isinstance(infile, (list, tuple)):
1247
infile = list(infile)
781
1249
elif isinstance(infile, dict):
782
1250
# initialise self
783
1251
# the Section class handles creating subsections
784
1252
if isinstance(infile, ConfigObj):
785
1253
# get a copy of our ConfigObj
786
1254
infile = infile.dict()
787
1256
for entry in infile:
788
1257
self[entry] = infile[entry]
790
1258
del self._errors
791
if defaults['configspec'] is not None:
792
self._handle_configspec(defaults['configspec'])
1260
if configspec is not None:
1261
self._handle_configspec(configspec)
794
1263
self.configspec = None
796
elif hasattr(infile, 'seek'):
797
# this supports StringIO instances and even file objects
798
self.filename = infile
800
infile = infile.readlines()
801
self.filename.seek(0)
803
raise TypeError, ('infile must be a filename,'
804
' StringIO instance, or a file as a list.')
806
# strip trailing '\n' from lines
807
infile = [line.rstrip('\n') for line in infile]
809
# remove the UTF8 BOM if it is there
810
# FIXME: support other BOM
811
if infile and infile[0].startswith(BOM_UTF8):
812
infile[0] = infile[0][3:]
1266
elif getattr(infile, 'read', MISSING) is not MISSING:
1267
# This supports file like objects
1268
infile = infile.read() or []
1269
# needs splitting into lines - but needs doing *after* decoding
1270
# in case it's not an 8 bit encoding
1272
raise TypeError('infile must be a filename, file like object, or list of lines.')
1275
# don't do it for the empty ConfigObj
1276
infile = self._handle_bom(infile)
1277
# infile is now *always* a list
1279
# Set the newlines attribute (first line ending it finds)
1280
# and strip trailing '\n' or '\r' from lines
1282
if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1284
for end in ('\r\n', '\n', '\r'):
1285
if line.endswith(end):
1290
infile = [line.rstrip('\r\n') for line in infile]
817
1292
self._parse(infile)
818
1293
# if we had any errors, now is the time to raise them
819
1294
if self._errors:
820
error = ConfigObjError("Parsing failed.")
1295
info = "at line %s." % self._errors[0].line_number
1296
if len(self._errors) > 1:
1297
msg = "Parsing failed with several errors.\nFirst error %s" % info
1298
error = ConfigObjError(msg)
1300
error = self._errors[0]
821
1301
# set the errors attribute; it's a list of tuples:
822
1302
# (error_type, message, line_number)
823
1303
error.errors = self._errors
827
1307
# delete private attributes
828
1308
del self._errors
830
if defaults['configspec'] is None:
1310
if configspec is None:
831
1311
self.configspec = None
833
self._handle_configspec(defaults['configspec'])
1313
self._handle_configspec(configspec)
1316
def _initialise(self, options=None):
1318
options = OPTION_DEFAULTS
1320
# initialise a few variables
1321
self.filename = None
1323
self.raise_errors = options['raise_errors']
1324
self.interpolation = options['interpolation']
1325
self.list_values = options['list_values']
1326
self.create_empty = options['create_empty']
1327
self.file_error = options['file_error']
1328
self.stringify = options['stringify']
1329
self.indent_type = options['indent_type']
1330
self.encoding = options['encoding']
1331
self.default_encoding = options['default_encoding']
1333
self.newlines = None
1334
self.write_empty_values = options['write_empty_values']
1335
self.unrepr = options['unrepr']
1337
self.initial_comment = []
1338
self.final_comment = []
1339
self.configspec = None
1342
self.list_values = False
1344
# Clear section attributes as well
1345
Section._initialise(self)
1349
return ('ConfigObj({%s})' %
1350
', '.join([('%s: %s' % (repr(key), repr(self[key])))
1351
for key in (self.scalars + self.sections)]))
1354
def _handle_bom(self, infile):
1356
Handle any BOM, and decode if necessary.
1358
If an encoding is specified, that *must* be used - but the BOM should
1359
still be removed (and the BOM attribute set).
1361
(If the encoding is wrongly specified, then a BOM for an alternative
1362
encoding won't be discovered or removed.)
1364
If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1365
removed. The BOM attribute will be set. UTF16 will be decoded to
1368
NOTE: This method must not be called with an empty ``infile``.
1370
Specifying the *wrong* encoding is likely to cause a
1371
``UnicodeDecodeError``.
1373
``infile`` must always be returned as a list of lines, but may be
1374
passed in as a single string.
1376
if ((self.encoding is not None) and
1377
(self.encoding.lower() not in BOM_LIST)):
1378
# No need to check for a BOM
1379
# the encoding specified doesn't have one
1381
return self._decode(infile, self.encoding)
1383
if isinstance(infile, (list, tuple)):
1387
if self.encoding is not None:
1388
# encoding explicitly supplied
1389
# And it could have an associated BOM
1390
# TODO: if encoding is just UTF16 - we ought to check for both
1391
# TODO: big endian and little endian versions.
1392
enc = BOM_LIST[self.encoding.lower()]
1394
# For UTF16 we try big endian and little endian
1395
for BOM, (encoding, final_encoding) in BOMS.items():
1396
if not final_encoding:
1399
if infile.startswith(BOM):
1402
# Don't need to remove BOM
1403
return self._decode(infile, encoding)
1405
# If we get this far, will *probably* raise a DecodeError
1406
# As it doesn't appear to start with a BOM
1407
return self._decode(infile, self.encoding)
1411
if not line.startswith(BOM):
1412
return self._decode(infile, self.encoding)
1414
newline = line[len(BOM):]
1417
if isinstance(infile, (list, tuple)):
1422
return self._decode(infile, self.encoding)
1424
# No encoding specified - so we need to check for UTF8/UTF16
1425
for BOM, (encoding, final_encoding) in BOMS.items():
1426
if not line.startswith(BOM):
1430
self.encoding = final_encoding
1431
if not final_encoding:
1435
newline = line[len(BOM):]
1436
if isinstance(infile, (list, tuple)):
1440
# UTF8 - don't decode
1441
if isinstance(infile, basestring):
1442
return infile.splitlines(True)
1445
# UTF16 - have to decode
1446
return self._decode(infile, encoding)
1448
# No BOM discovered and no encoding specified, just return
1449
if isinstance(infile, basestring):
1450
# infile read from a file will be a single string
1451
return infile.splitlines(True)
1455
def _a_to_u(self, aString):
1456
"""Decode ASCII strings to unicode if a self.encoding is specified."""
1458
return aString.decode('ascii')
1463
def _decode(self, infile, encoding):
1465
Decode infile to unicode. Using the specified encoding.
1467
if is a string, it also needs converting to a list.
1469
if isinstance(infile, basestring):
1471
# NOTE: Could raise a ``UnicodeDecodeError``
1472
return infile.decode(encoding).splitlines(True)
1473
for i, line in enumerate(infile):
1474
if not isinstance(line, unicode):
1475
# NOTE: The isinstance test here handles mixed lists of unicode/string
1476
# NOTE: But the decode will break on any non-string values
1477
# NOTE: Or could raise a ``UnicodeDecodeError``
1478
infile[i] = line.decode(encoding)
1482
def _decode_element(self, line):
1483
"""Decode element to unicode if necessary."""
1484
if not self.encoding:
1486
if isinstance(line, str) and self.default_encoding:
1487
return line.decode(self.default_encoding)
1491
def _str(self, value):
1493
Used by ``stringify`` within validate, to turn non-string values
1496
if not isinstance(value, basestring):
835
1502
def _parse(self, infile):
837
Actually parse the config file
839
Testing Interpolation
844
... 'userdir': 'c:\\\\home',
849
... 'a': '%(datadir)s\\\\some path\\\\file.py',
850
... 'b': '%(userdir)s\\\\some path\\\\file.py',
852
... 'd': '%(not_here)s',
855
>>> c['section']['DEFAULT'] = {
856
... 'datadir': 'c:\\\\silly_test',
857
... 'a': 'hello - %(b)s',
859
>>> c['section']['a'] == 'c:\\\\silly_test\\\\some path\\\\file.py'
861
>>> c['section']['b'] == 'c:\\\\home\\\\some path\\\\file.py'
863
>>> c['section']['c'] == 'Yo hello - goodbye'
866
Switching Interpolation Off
868
>>> c.interpolation = False
869
>>> c['section']['a'] == '%(datadir)s\\\\some path\\\\file.py'
871
>>> c['section']['b'] == '%(userdir)s\\\\some path\\\\file.py'
873
>>> c['section']['c'] == 'Yo %(a)s'
876
Testing the interpolation errors.
878
>>> c.interpolation = True
879
>>> c['section']['d']
880
Traceback (most recent call last):
881
MissingInterpolationOption: missing option "not_here" in interpolation.
882
>>> c['section']['e']
883
Traceback (most recent call last):
884
InterpolationDepthError: max interpolation depth exceeded in value "%(c)s".
888
>>> i._quote('\"""\'\'\'')
889
Traceback (most recent call last):
890
SyntaxError: EOF while scanning triple-quoted string
892
... i._quote('\\n', multiline=False)
893
... except ConfigObjError, e:
895
'Value "\\n" cannot be safely quoted.'
896
>>> k._quote(' "\' ', multiline=False)
897
Traceback (most recent call last):
898
SyntaxError: EOL while scanning single-quoted string
900
Testing with "stringify" off.
901
>>> c.stringify = False
903
Traceback (most recent call last):
904
TypeError: Value is not a string "1".
1503
"""Actually parse the config file."""
1504
temp_list_values = self.list_values
1506
self.list_values = False
906
1508
comment_list = []
907
1509
done_start = False
908
1510
this_section = self
909
1511
maxline = len(infile) - 1
911
1513
reset_comment = False
912
1515
while cur_index < maxline:
913
1516
if reset_comment:
914
1517
comment_list = []
1002
1605
'Parse error in value at line %s.',
1003
1606
ParseError, infile, cur_index)
1612
value = unrepr(value)
1613
except Exception, e:
1614
if type(e) == UnknownType:
1615
msg = 'Unknown name or type in value at line %s.'
1617
msg = 'Parse error in value at line %s.'
1618
self._handle_error(msg, UnreprError, infile,
1006
# extract comment and lists
1008
(value, comment) = self._handle_value(value)
1011
'Parse error in value at line %s.',
1012
ParseError, infile, cur_index)
1625
value = unrepr(value)
1626
except Exception, e:
1627
if isinstance(e, UnknownType):
1628
msg = 'Unknown name or type in value at line %s.'
1630
msg = 'Parse error in value at line %s.'
1631
self._handle_error(msg, UnreprError, infile,
1635
# extract comment and lists
1637
(value, comment) = self._handle_value(value)
1640
'Parse error in value at line %s.',
1641
ParseError, infile, cur_index)
1015
## print >> sys.stderr, sline
1016
1644
key = self._unquote(key)
1017
if this_section.has_key(key):
1645
if key in this_section:
1018
1646
self._handle_error(
1019
1647
'Duplicate keyword name at line %s.',
1020
1648
DuplicateError, infile, cur_index)
1023
## print >> sys.stderr, this_section.name
1024
this_section[key] = value
1651
# we set unrepr because if we have got this far we will never
1652
# be creating a new section
1653
this_section.__setitem__(key, value, unrepr=True)
1025
1654
this_section.inline_comments[key] = comment
1026
1655
this_section.comments[key] = comment_list
1027
## print >> sys.stderr, key, this_section[key]
1028
## if this_section.name is not None:
1029
## print >> sys.stderr, this_section
1030
## print >> sys.stderr, this_section.parent
1031
## print >> sys.stderr, this_section.parent[this_section.name]
1034
# it neither matched as a keyword
1035
# or a section marker
1037
'Invalid line at line "%s".',
1038
ParseError, infile, cur_index)
1039
1658
if self.indent_type is None:
1040
1659
# no indentation used, set the type accordingly
1041
1660
self.indent_type = ''
1042
1662
# preserve the final comment
1043
self.final_comment = comment_list
1663
if not self and not self.initial_comment:
1664
self.initial_comment = comment_list
1665
elif not reset_comment:
1666
self.final_comment = comment_list
1667
self.list_values = temp_list_values
1045
1670
def _match_depth(self, sect, depth):
1047
1672
Given a section and a depth level, walk back through the sections
1048
1673
parents to see if the depth level matches a previous section.
1050
1675
Return a reference to the right section,
1051
1676
or raise a SyntaxError.
1053
1678
while depth < sect.depth:
1054
1679
if sect is sect.parent:
1055
1680
# we've reached the top level already
1057
1682
sect = sect.parent
1058
1683
if sect.depth == depth:
1060
1685
# shouldn't get here
1063
1689
def _handle_error(self, text, ErrorClass, infile, cur_index):
1065
1691
Handle an error according to the error settings.
1067
1693
Either raise the error or store it.
1068
1694
The error will have occured at ``cur_index``
1070
1696
line = infile[cur_index]
1071
1698
message = text % cur_index
1072
1699
error = ErrorClass(message, cur_index, line)
1073
1700
if self.raise_errors:
1077
1704
# reraise when parsing has finished
1078
1705
self._errors.append(error)
1080
1708
def _unquote(self, value):
1081
1709
"""Return an unquoted version of a value"""
1082
1710
if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1083
1711
value = value[1:-1]
1086
1715
def _quote(self, value, multiline=True):
1088
1717
Return a safely quoted version of a value.
1090
1719
Raise a ConfigObjError if the value cannot be safely quoted.
1091
1720
If multiline is ``True`` (default) then use triple quotes
1094
Don't quote values that don't need it.
1095
Recursively quote members of a list and return a comma joined list.
1096
Multiline is ``False`` for lists.
1097
Obey list syntax for empty and single member lists.
1723
* Don't quote values that don't need it.
1724
* Recursively quote members of a list and return a comma joined list.
1725
* Multiline is ``False`` for lists.
1726
* Obey list syntax for empty and single member lists.
1728
If ``list_values=False`` then the value is only quoted if it contains
1729
a ``\\n`` (is multiline) or '#'.
1731
If ``write_empty_values`` is set, and the value is an empty string, it
1099
if isinstance(value, (list, tuple)):
1734
if multiline and self.write_empty_values and value == '':
1735
# Only if multiline is set, so that it is used for values not
1736
# keys, and not values that are part of a list
1739
if multiline and isinstance(value, (list, tuple)):
1102
1742
elif len(value) == 1:
1103
1743
return self._quote(value[0], multiline=False) + ','
1104
return ','.join([self._quote(val, multiline=False)
1744
return ', '.join([self._quote(val, multiline=False)
1105
1745
for val in value])
1106
if not isinstance(value, StringTypes):
1746
if not isinstance(value, basestring):
1107
1747
if self.stringify:
1108
1748
value = str(value)
1110
raise TypeError, 'Value "%s" is not a string.' % value
1114
wspace_plus = ' \r\t\n\v\t\'"'
1750
raise TypeError('Value "%s" is not a string.' % value)
1119
if not (multiline and
1120
((("'" in value) and ('"' in value)) or ('\n' in value))):
1755
no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1756
need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1757
hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1758
check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1760
if check_for_single:
1761
if not self.list_values:
1762
# we don't quote if ``list_values=False``
1121
1764
# for normal values either single or double quotes will do
1123
raise ConfigObjError, ('Value "%s" cannot be safely quoted.' %
1125
if ((value[0] not in wspace_plus) and
1766
# will only happen if multiline is off - e.g. '\n' in key
1767
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1768
elif ((value[0] not in wspace_plus) and
1126
1769
(value[-1] not in wspace_plus) and
1127
1770
(',' not in value)):
1130
if ("'" in value) and ('"' in value):
1131
raise ConfigObjError, (
1132
'Value "%s" cannot be safely quoted.' % value)
1773
quot = self._get_single_quote(value)
1138
1775
# if value has '\n' or "'" *and* '"', it will need triple quotes
1139
if (value.find('"""') != -1) and (value.find("'''") != -1):
1140
raise ConfigObjError, (
1141
'Value "%s" cannot be safely quoted.' % value)
1142
if value.find('"""') == -1:
1776
quot = self._get_triple_quote(value)
1778
if quot == noquot and '#' in value and self.list_values:
1779
quot = self._get_single_quote(value)
1146
1781
return quot % value
1784
def _get_single_quote(self, value):
1785
if ("'" in value) and ('"' in value):
1786
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1794
def _get_triple_quote(self, value):
1795
if (value.find('"""') != -1) and (value.find("'''") != -1):
1796
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1797
# upstream version (up to version 4.7.2) has the bug with incorrect quoting;
1798
# fixed in our copy based on the suggestion of ConfigObj's author
1799
if value.find('"""') == -1:
1148
1806
def _handle_value(self, value):
1150
1808
Given a value string, unquote, remove comment,
1151
1809
handle lists. (including empty and single member lists)
1153
Testing list values.
1155
>>> testconfig3 = '''
1158
... c = test1, test2 , test3
1159
... d = test1, test2, test3,
1161
>>> d = ConfigObj(testconfig3.split('\\n'), raise_errors=True)
1164
>>> d['b'] == ['test']
1166
>>> d['c'] == ['test1', 'test2', 'test3']
1168
>>> d['d'] == ['test1', 'test2', 'test3']
1171
Testing with list values off.
1174
... testconfig3.split('\\n'),
1175
... raise_errors=True,
1176
... list_values=False)
1179
>>> e['b'] == 'test,'
1181
>>> e['c'] == 'test1, test2 , test3'
1183
>>> e['d'] == 'test1, test2, test3,'
1186
Testing creating from a dictionary.
1209
>>> g = ConfigObj(f)
1213
Testing we correctly detect badly built list values (4 of them).
1215
>>> testconfig4 = '''
1219
... dummy = ,,hello, goodbye
1222
... ConfigObj(testconfig4.split('\\n'))
1223
... except ConfigObjError, e:
1227
Testing we correctly detect badly quoted values (4 of them).
1229
>>> testconfig5 = '''
1230
... config = "hello # comment
1232
... fish = 'goodbye # comment
1233
... dummy = "hello again
1236
... ConfigObj(testconfig5.split('\\n'))
1237
... except ConfigObjError, e:
1812
# Parsing a configspec so don't handle comments
1241
1814
# do we look for lists in values ?
1242
1815
if not self.list_values:
1243
1816
mat = self._nolistvalue.match(value)
1244
1817
if mat is None:
1246
(value, comment) = mat.groups()
1247
# FIXME: unquoting here can be a source of error
1248
return (self._unquote(value), comment)
1819
# NOTE: we don't unquote here
1249
1822
mat = self._valueexp.match(value)
1250
1823
if mat is None:
1251
1824
# the value is badly constructed, probably badly quoted,
1252
1825
# or an invalid list
1254
1827
(list_values, single, empty_list, comment) = mat.groups()
1255
1828
if (list_values == '') and (single is None):
1256
1829
# change this if you want to accept empty values
1258
1831
# NOTE: note there is no error handling from here if the regex
1259
1832
# is wrong: then incorrect values will slip through
1260
1833
if empty_list is not None:
1261
1834
# the single comma - meaning an empty list
1262
1835
return ([], comment)
1263
1836
if single is not None:
1264
single = self._unquote(single)
1837
# handle empty values
1838
if list_values and not single:
1839
# FIXME: the '' is a workaround because our regex now matches
1840
# '' at the end of a list if it has a trailing comma
1843
single = single or '""'
1844
single = self._unquote(single)
1265
1845
if list_values == '':
1266
1846
# not a list value
1267
1847
return (single, comment)
1321
1880
# we've got to the end of the config, oops...
1323
1882
mat = multi_line.match(line)
1324
1883
if mat is None:
1325
1884
# a badly formed line
1327
1886
(value, comment) = mat.groups()
1328
1887
return (newvalue + value, comment, cur_index)
1330
1890
def _handle_configspec(self, configspec):
1331
1891
"""Parse the configspec."""
1333
configspec = ConfigObj(
1338
except ConfigObjError, e:
1339
# FIXME: Should these errors have a reference
1340
# to the already parsed ConfigObj ?
1341
raise ConfigspecError('Parsing configspec failed: %s' % e)
1343
raise IOError('Reading configspec failed: %s' % e)
1344
self._set_configspec_value(configspec, self)
1346
def _set_configspec_value(self, configspec, section):
1347
"""Used to recursively set configspec values."""
1348
if '__many__' in configspec.sections:
1349
section.configspec['__many__'] = configspec['__many__']
1350
if len(configspec.sections) > 1:
1351
# FIXME: can we supply any useful information here ?
1352
raise RepeatSectionError
1353
for entry in configspec.scalars:
1354
section.configspec[entry] = configspec[entry]
1892
# FIXME: Should we check that the configspec was created with the
1893
# correct settings ? (i.e. ``list_values=False``)
1894
if not isinstance(configspec, ConfigObj):
1896
configspec = ConfigObj(configspec,
1900
except ConfigObjError, e:
1901
# FIXME: Should these errors have a reference
1902
# to the already parsed ConfigObj ?
1903
raise ConfigspecError('Parsing configspec failed: %s' % e)
1905
raise IOError('Reading configspec failed: %s' % e)
1907
self.configspec = configspec
1911
def _set_configspec(self, section, copy):
1913
Called by validate. Handles setting the configspec on subsections
1914
including sections to be validated by __many__
1916
configspec = section.configspec
1917
many = configspec.get('__many__')
1918
if isinstance(many, dict):
1919
for entry in section.sections:
1920
if entry not in configspec:
1921
section[entry].configspec = many
1355
1923
for entry in configspec.sections:
1356
1924
if entry == '__many__':
1358
if not section.has_key(entry):
1360
self._set_configspec_value(configspec[entry], section[entry])
1362
def _handle_repeat(self, section, configspec):
1363
"""Dynamically assign configspec for repeated section."""
1365
section_keys = configspec.sections
1366
scalar_keys = configspec.scalars
1367
except AttributeError:
1368
section_keys = [entry for entry in configspec
1369
if isinstance(configspec[entry], dict)]
1370
scalar_keys = [entry for entry in configspec
1371
if not isinstance(configspec[entry], dict)]
1372
if '__many__' in section_keys and len(section_keys) > 1:
1373
# FIXME: can we supply any useful information here ?
1374
raise RepeatSectionError
1377
for entry in scalar_keys:
1378
val = configspec[entry]
1379
scalars[entry] = val
1380
for entry in section_keys:
1381
val = configspec[entry]
1382
if entry == '__many__':
1383
scalars[entry] = val
1385
sections[entry] = val
1387
section.configspec = scalars
1388
for entry in sections:
1389
if not section.has_key(entry):
1391
self._handle_repeat(section[entry], sections[entry])
1926
if entry not in section:
1930
section.comments[entry] = configspec.comments.get(entry, [])
1931
section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1933
# Could be a scalar when we expect a section
1934
if isinstance(section[entry], Section):
1935
section[entry].configspec = configspec[entry]
1393
1938
def _write_line(self, indent_string, entry, this_entry, comment):
1394
1939
"""Write an individual line, for the write method"""
1395
return '%s%s = %s%s' % (
1397
self._quote(entry, multiline=False),
1398
self._quote(this_entry),
1940
# NOTE: the calls to self._quote here handles non-StringType values.
1942
val = self._decode_element(self._quote(this_entry))
1944
val = repr(this_entry)
1945
return '%s%s%s%s%s' % (indent_string,
1946
self._decode_element(self._quote(entry, multiline=False)),
1947
self._a_to_u(' = '),
1949
self._decode_element(comment))
1401
1952
def _write_marker(self, indent_string, depth, entry, comment):
1402
1953
"""Write a section marker line"""
1403
return '%s%s%s%s%s' % (
1406
self._quote(entry, multiline=False),
1954
return '%s%s%s%s%s' % (indent_string,
1955
self._a_to_u('[' * depth),
1956
self._quote(self._decode_element(entry), multiline=False),
1957
self._a_to_u(']' * depth),
1958
self._decode_element(comment))
1410
1961
def _handle_comment(self, comment):
1412
Deal with a comment.
1414
>>> filename = a.filename
1415
>>> a.filename = None
1416
>>> values = a.write()
1418
>>> while index < 23:
1420
... line = values[index-1]
1421
... assert line.endswith('# comment ' + str(index))
1422
>>> a.filename = filename
1424
>>> start_comment = ['# Initial Comment', '', '#']
1425
>>> end_comment = ['', '#', '# Final Comment']
1426
>>> newconfig = start_comment + testconfig1.split('\\n') + end_comment
1427
>>> nc = ConfigObj(newconfig)
1428
>>> nc.initial_comment
1429
['# Initial Comment', '', '#']
1430
>>> nc.final_comment
1431
['', '#', '# Final Comment']
1432
>>> nc.initial_comment == start_comment
1434
>>> nc.final_comment == end_comment
1962
"""Deal with a comment."""
1437
1963
if not comment:
1439
if self.indent_type == '\t':
1442
start = ' ' * NUM_INDENT_SPACES
1965
start = self.indent_type
1443
1966
if not comment.startswith('#'):
1967
start += self._a_to_u(' # ')
1445
1968
return (start + comment)
1447
def _compute_indent_string(self, depth):
1449
Compute the indent string, according to current indent_type and depth
1451
if self.indent_type == '':
1452
# no indentation at all
1454
if self.indent_type == '\t':
1456
if self.indent_type == ' ':
1457
return ' ' * NUM_INDENT_SPACES * depth
1460
1971
# Public methods
1462
def write(self, section=None):
1973
def write(self, outfile=None, section=None):
1464
1975
Write the current ConfigObj as a file
1466
1977
tekNico: FIXME: use StringIO instead of real files
1468
1979
>>> filename = a.filename
1469
1980
>>> a.filename = 'test.ini'
1471
1982
>>> a.filename = filename
1472
1983
>>> a == ConfigObj('test.ini', raise_errors=True)
1474
>>> os.remove('test.ini')
1475
>>> b.filename = 'test.ini'
1477
>>> b == ConfigObj('test.ini', raise_errors=True)
1479
>>> os.remove('test.ini')
1480
>>> i.filename = 'test.ini'
1482
>>> i == ConfigObj('test.ini', raise_errors=True)
1484
>>> os.remove('test.ini')
1486
>>> a['DEFAULT'] = {'a' : 'fish'}
1487
>>> a['a'] = '%(a)s'
1489
['a = %(a)s', '[DEFAULT]', 'a = fish']
1492
1986
if self.indent_type is None:
1493
1987
# this can be true if initialised from a dictionary
1494
1988
self.indent_type = DEFAULT_INDENT_TYPE
1991
cs = self._a_to_u('#')
1992
csp = self._a_to_u('# ')
1498
1993
if section is None:
1499
1994
int_val = self.interpolation
1500
1995
self.interpolation = False
1503
1997
for line in self.initial_comment:
1998
line = self._decode_element(line)
1504
1999
stripped_line = line.strip()
1505
if stripped_line and not stripped_line.startswith('#'):
2000
if stripped_line and not stripped_line.startswith(cs):
1507
2002
out.append(line)
1509
indent_string = self._compute_indent_string(section.depth)
2004
indent_string = self.indent_type * section.depth
1510
2005
for entry in (section.scalars + section.sections):
1511
2006
if entry in section.defaults:
1512
2007
# don't write out default values
1514
2009
for comment_line in section.comments[entry]:
1515
comment_line = comment_line.lstrip()
1516
if comment_line and not comment_line.startswith('#'):
1517
comment_line = '#' + comment_line
2010
comment_line = self._decode_element(comment_line.lstrip())
2011
if comment_line and not comment_line.startswith(cs):
2012
comment_line = csp + comment_line
1518
2013
out.append(indent_string + comment_line)
1519
2014
this_entry = section[entry]
1520
2015
comment = self._handle_comment(section.inline_comments[entry])
1522
2017
if isinstance(this_entry, dict):
1524
2019
out.append(self._write_marker(
1526
2021
this_entry.depth,
1529
out.extend(self.write(this_entry))
2024
out.extend(self.write(section=this_entry))
1531
2026
out.append(self._write_line(
1538
2033
for line in self.final_comment:
2034
line = self._decode_element(line)
1539
2035
stripped_line = line.strip()
1540
if stripped_line and not stripped_line.startswith('#'):
2036
if stripped_line and not stripped_line.startswith(cs):
1542
2038
out.append(line)
1544
if int_val != 'test':
1545
2039
self.interpolation = int_val
1547
if (return_list) or (self.filename is None):
1550
if isinstance(self.filename, StringTypes):
1551
h = open(self.filename, 'w')
1552
h.write(self.BOM or '')
1553
h.write('\n'.join(out))
2041
if section is not self:
2044
if (self.filename is None) and (outfile is None):
2045
# output a list of lines
2046
# might need to encode
2047
# NOTE: This will *screw* UTF16, each line will start with the BOM
2049
out = [l.encode(self.encoding) for l in out]
2050
if (self.BOM and ((self.encoding is None) or
2051
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2055
out[0] = BOM_UTF8 + out[0]
2058
# Turn the list to a string, joined with correct newlines
2059
newline = self.newlines or os.linesep
2060
output = self._a_to_u(newline).join(out)
2062
output = output.encode(self.encoding)
2063
if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2065
output = BOM_UTF8 + output
2067
if not output.endswith(newline):
2069
if outfile is not None:
2070
outfile.write(output)
2072
h = open(self.filename, 'wb')
1556
self.filename.seek(0)
1557
self.filename.write(self.BOM or '')
1558
self.filename.write('\n'.join(out))
1559
# if we have a stored file object (or StringIO)
1560
# we *don't* close it
1562
def validate(self, validator, section=None):
2077
def validate(self, validator, preserve_errors=False, copy=False,
1564
2080
Test the ConfigObj against a configspec.
1566
2082
It uses the ``validator`` object from *validate.py*.
1568
2084
To run ``validate`` on the current ConfigObj, call: ::
1570
2086
test = config.validate(validator)
1572
2088
(Normally having previously passed in the configspec when the ConfigObj
1573
2089
was created - you can dynamically assign a dictionary of checks to the
1574
2090
``configspec`` attribute of a section though).
1576
2092
It returns ``True`` if everything passes, or a dictionary of
1577
2093
pass/fails (True/False). If every member of a subsection passes, it
1578
2094
will just have the value ``True``. (It also returns ``False`` if all
1581
2097
In addition, it converts the values from strings to their native
1582
2098
types if their checks pass (and ``stringify`` is set).
1585
... from validate import Validator
1586
... except ImportError:
1587
... print >> sys.stderr, 'Cannot import the Validator object, skipping the realted tests'
1604
... '''.split('\\n')
1605
... configspec = '''
1606
... test1='integer(30,50)'
1609
... test4='float(6.0)'
1611
... test1='integer(30,50)'
1614
... test4='float(6.0)'
1616
... test1='integer(30,50)'
1619
... test4='float(6.0)'
1620
... '''.split('\\n')
1621
... val = Validator()
1622
... c1 = ConfigObj(config, configspec=configspec)
1623
... test = c1.validate(val)
1634
... 'sub section': {
1643
>>> val.check(c1.configspec['test4'], c1['test4'])
1644
Traceback (most recent call last):
1645
VdtValueTooSmallError: the value "5.0" is too small.
1647
>>> val_test_config = '''
1652
... key2 = 1.1, 3.0, 17, 6.8
1655
... key2 = True'''.split('\\n')
1656
>>> val_test_configspec = '''
1661
... key2 = float_list(4)
1663
... key = option(option1, option2)
1664
... key2 = boolean'''.split('\\n')
1665
>>> val_test = ConfigObj(val_test_config, configspec=val_test_configspec)
1666
>>> val_test.validate(val)
1668
>>> val_test['key'] = 'text not a digit'
1669
>>> val_res = val_test.validate(val)
1670
>>> val_res == {'key2': True, 'section': True, 'key': False}
1672
>>> configspec = '''
1673
... test1='integer(30,50, default=40)'
1674
... test2='string(default="hello")'
1675
... test3='integer(default=3)'
1676
... test4='float(6.0, default=6.0)'
1678
... test1='integer(30,50, default=40)'
1679
... test2='string(default="hello")'
1680
... test3='integer(default=3)'
1681
... test4='float(6.0, default=6.0)'
1683
... test1='integer(30,50, default=40)'
1684
... test2='string(default="hello")'
1685
... test3='integer(default=3)'
1686
... test4='float(6.0, default=6.0)'
1687
... '''.split('\\n')
1688
>>> default_test = ConfigObj(['test1=30'], configspec=configspec)
1690
{'test1': '30', 'section': {'sub section': {}}}
1691
>>> default_test.validate(val)
1693
>>> default_test == {
1695
... 'test2': 'hello',
1700
... 'test2': 'hello',
1703
... 'sub section': {
1706
... 'test2': 'hello',
1713
Now testing with repeated sections : BIG TEST
1715
>>> repeated_1 = '''
1717
... [[__many__]] # spec for a dog
1718
... fleas = boolean(default=True)
1719
... tail = option(long, short, default=long)
1720
... name = string(default=rover)
1721
... [[[__many__]]] # spec for a puppy
1722
... name = string(default="son of rover")
1723
... age = float(default=0.0)
1725
... [[__many__]] # spec for a cat
1726
... fleas = boolean(default=True)
1727
... tail = option(long, short, default=short)
1728
... name = string(default=pussy)
1729
... [[[__many__]]] # spec for a kitten
1730
... name = string(default="son of pussy")
1731
... age = float(default=0.0)
1732
... '''.split('\\n')
1733
>>> repeated_2 = '''
1736
... # blank dogs with puppies
1737
... # should be filled in by the configspec
1752
... # blank cats with kittens
1753
... # should be filled in by the configspec
1766
... '''.split('\\n')
1767
>>> repeated_3 = '''
1778
... '''.split('\\n')
1779
>>> repeated_4 = '''
1782
... name = string(default=Michael)
1783
... age = float(default=0.0)
1784
... sex = option(m, f, default=m)
1785
... '''.split('\\n')
1786
>>> repeated_5 = '''
1789
... fleas = boolean(default=True)
1790
... tail = option(long, short, default=short)
1791
... name = string(default=pussy)
1792
... [[[description]]]
1793
... height = float(default=3.3)
1794
... weight = float(default=6)
1796
... fur = option(black, grey, brown, "tortoise shell", default=black)
1797
... condition = integer(0,10, default=5)
1798
... '''.split('\\n')
1799
>>> from validate import Validator
1800
>>> val= Validator()
1801
>>> repeater = ConfigObj(repeated_2, configspec=repeated_1)
1802
>>> repeater.validate(val)
1809
... 'name': 'rover',
1810
... 'puppy1': {'name': 'son of rover', 'age': 0.0},
1811
... 'puppy2': {'name': 'son of rover', 'age': 0.0},
1812
... 'puppy3': {'name': 'son of rover', 'age': 0.0},
1817
... 'name': 'rover',
1818
... 'puppy1': {'name': 'son of rover', 'age': 0.0},
1819
... 'puppy2': {'name': 'son of rover', 'age': 0.0},
1820
... 'puppy3': {'name': 'son of rover', 'age': 0.0},
1825
... 'name': 'rover',
1826
... 'puppy1': {'name': 'son of rover', 'age': 0.0},
1827
... 'puppy2': {'name': 'son of rover', 'age': 0.0},
1828
... 'puppy3': {'name': 'son of rover', 'age': 0.0},
1834
... 'tail': 'short',
1835
... 'name': 'pussy',
1836
... 'kitten1': {'name': 'son of pussy', 'age': 0.0},
1837
... 'kitten2': {'name': 'son of pussy', 'age': 0.0},
1838
... 'kitten3': {'name': 'son of pussy', 'age': 0.0},
1842
... 'tail': 'short',
1843
... 'name': 'pussy',
1844
... 'kitten1': {'name': 'son of pussy', 'age': 0.0},
1845
... 'kitten2': {'name': 'son of pussy', 'age': 0.0},
1846
... 'kitten3': {'name': 'son of pussy', 'age': 0.0},
1850
... 'tail': 'short',
1851
... 'name': 'pussy',
1852
... 'kitten1': {'name': 'son of pussy', 'age': 0.0},
1853
... 'kitten2': {'name': 'son of pussy', 'age': 0.0},
1854
... 'kitten3': {'name': 'son of pussy', 'age': 0.0},
1859
>>> repeater = ConfigObj(repeated_3, configspec=repeated_1)
1860
>>> repeater.validate(val)
1864
... 'cat1': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
1865
... 'cat2': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
1866
... 'cat3': {'fleas': True, 'tail': 'short', 'name': 'pussy'},
1869
... 'dog1': {'fleas': True, 'tail': 'long', 'name': 'rover'},
1870
... 'dog2': {'fleas': True, 'tail': 'long', 'name': 'rover'},
1871
... 'dog3': {'fleas': True, 'tail': 'long', 'name': 'rover'},
1875
>>> repeater = ConfigObj(configspec=repeated_4)
1876
>>> repeater['Michael'] = {}
1877
>>> repeater.validate(val)
1880
... 'Michael': {'age': 0.0, 'name': 'Michael', 'sex': 'm'},
1883
>>> repeater = ConfigObj(repeated_3, configspec=repeated_5)
1885
... 'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
1886
... 'cats': {'cat1': {}, 'cat2': {}, 'cat3': {}},
1889
>>> repeater.validate(val)
1892
... 'dogs': {'dog1': {}, 'dog2': {}, 'dog3': {}},
1896
... 'tail': 'short',
1897
... 'name': 'pussy',
1898
... 'description': {
1900
... 'height': 3.2999999999999998,
1901
... 'coat': {'fur': 'black', 'condition': 5},
1906
... 'tail': 'short',
1907
... 'name': 'pussy',
1908
... 'description': {
1910
... 'height': 3.2999999999999998,
1911
... 'coat': {'fur': 'black', 'condition': 5},
1916
... 'tail': 'short',
1917
... 'name': 'pussy',
1918
... 'description': {
1920
... 'height': 3.2999999999999998,
1921
... 'coat': {'fur': 'black', 'condition': 5},
1928
Test that interpolation is preserved for validated string values.
1930
>>> t['DEFAULT'] = {}
1931
>>> t['DEFAULT']['test'] = 'a'
1932
>>> t['test'] = '%(test)s'
1936
>>> t.configspec = {'test': 'string'}
1939
>>> t.interpolation = False
1941
{'test': '%(test)s', 'DEFAULT': {'test': 'a'}}
1943
FIXME: Above tests will fail if we couldn't import Validator (the ones
1944
that don't raise errors will produce different output and still fail as
2100
If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2101
of a marking a fail with a ``False``, it will preserve the actual
2102
exception object. This can contain info about the reason for failure.
2103
For example the ``VdtValueTooSmallError`` indicates that the value
2104
supplied was too small. If a value (or section) is missing it will
2105
still be marked as ``False``.
2107
You must have the validate module to use ``preserve_errors=True``.
2109
You can then use the ``flatten_errors`` function to turn your nested
2110
results dictionary into a flattened list of failures - useful for
2111
displaying meaningful error messages.
1947
2113
if section is None:
1948
2114
if self.configspec is None:
1949
raise ValueError, 'No configspec supplied.'
2115
raise ValueError('No configspec supplied.')
2117
# We do this once to remove a top level dependency on the validate module
2118
# Which makes importing configobj faster
2119
from validate import VdtMissingValue
2120
self._vdtMissingValue = VdtMissingValue
1952
spec_section = section.configspec
1953
if '__many__' in section.configspec:
1954
many = spec_section['__many__']
1955
# dynamically assign the configspecs
1956
# for the sections below
1957
for entry in section.sections:
1958
self._handle_repeat(section[entry], many)
1963
for entry in spec_section:
1964
if entry == '__many__':
1966
if (not entry in section.scalars) or (entry in section.defaults):
1968
# or entries from defaults
1973
val = section[entry]
2125
section.initial_comment = section.configspec.initial_comment
2126
section.final_comment = section.configspec.final_comment
2127
section.encoding = section.configspec.encoding
2128
section.BOM = section.configspec.BOM
2129
section.newlines = section.configspec.newlines
2130
section.indent_type = section.configspec.indent_type
2133
configspec = section.configspec
2134
self._set_configspec(section, copy)
2136
def validate_entry(entry, spec, val, missing, ret_true, ret_false):
1975
check = validator.check(spec_section[entry],
2138
check = validator.check(spec,
1978
except validator.baseErrorClass:
2142
except validator.baseErrorClass, e:
2143
if not preserve_errors or isinstance(e, self._vdtMissingValue):
2146
# preserve the error
1980
2149
ret_true = False
1981
# MIKE: we want to raise all other exceptions, not just print ?
1982
## except Exception, err:
2152
section.default_values.pop(entry, None)
2153
except AttributeError:
2154
# For Python 2.2 compatibility
2156
del section.default_values[entry]
2161
section.default_values[entry] = validator.get_default_value(configspec[entry])
2162
except (KeyError, AttributeError):
2163
# No default or validator has no 'get_default_value' (e.g. SimpleVal)
1985
2166
ret_false = False
1986
2167
out[entry] = True
1987
2168
if self.stringify or missing:
2017
2271
elif ret_false:
2277
"""Clear ConfigObj instance and restore to 'freshly created' state."""
2280
# FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2281
# requires an empty dictionary
2282
self.configspec = None
2283
# Just to be sure ;-)
2284
self._original_configspec = None
2289
Reload a ConfigObj from file.
2291
This method raises a ``ReloadError`` if the ConfigObj doesn't have
2292
a filename attribute pointing to a file.
2294
if not isinstance(self.filename, basestring):
2297
filename = self.filename
2298
current_options = {}
2299
for entry in OPTION_DEFAULTS:
2300
if entry == 'configspec':
2302
current_options[entry] = getattr(self, entry)
2304
configspec = self._original_configspec
2305
current_options['configspec'] = configspec
2308
self._initialise(current_options)
2309
self._load(filename, configspec)
2022
2313
class SimpleVal(object):
2024
2315
A simple validator.
2025
2316
Can be used to check that all members expected are present.
2027
2318
To use it, provide a configspec with all your members in (the value given
2028
2319
will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2029
2320
method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2030
2321
members are present, or a dictionary with True/False meaning
2031
2322
present/missing. (Whole missing sections will be replaced with ``False``)
2033
>>> val = SimpleVal()
2049
... '''.split('\\n')
2050
>>> configspec = '''
2065
... '''.split('\\n')
2066
>>> o = ConfigObj(config, configspec=configspec)
2069
>>> o = ConfigObj(configspec=configspec)
2074
2325
def __init__(self):
2075
2326
self.baseErrorClass = ConfigObjError
2077
2328
def check(self, check, member, missing=False):
2078
2329
"""A dummy check method, always returns the value unchanged."""
2080
raise self.baseErrorClass
2331
raise self.baseErrorClass()
2083
# FIXME: test error code for badly built multiline values
2084
# FIXME: test handling of StringIO
2085
# FIXME: test interpolation with writing
2089
Dummy function to hold some of the doctests.
2126
... 'keys11': 'val1',
2127
... 'keys13': 'val3',
2128
... 'keys12': 'val2',
2131
... 'section 2 sub 1': {
2134
... 'keys21': 'val1',
2135
... 'keys22': 'val2',
2136
... 'keys23': 'val3',
2141
... 'a' = b # !"$%^&*(),::;'@~#= 33
2142
... "b" = b #= 6, 33
2143
... ''' .split('\\n')
2144
>>> t2 = ConfigObj(t)
2145
>>> assert t2 == {'a': 'b', 'b': 'b'}
2146
>>> t2.inline_comments['b'] = ''
2148
>>> assert t2.write() == ['','b = b', '']
2151
if __name__ == '__main__':
2152
# run the code tests in doctest format
2155
key1= val # comment 1
2156
key2= val # comment 2
2159
key1= val # comment 5
2160
key2= val # comment 6
2163
key1= val # comment 9
2164
key2= val # comment 10
2166
[[lev2ba]] # comment 12
2167
key1= val # comment 13
2169
[[lev2bb]] # comment 15
2170
key1= val # comment 16
2172
[lev1c] # comment 18
2174
[[lev2c]] # comment 20
2176
[[[lev3c]]] # comment 22
2177
key1 = val # comment 23"""
2183
["section 1"] # comment
2192
[['section 2 sub 1']]
2197
name1 = """ a single line value """ # comment
2198
name2 = \''' another single line value \''' # comment
2199
name3 = """ a single line value """
2200
name4 = \''' another single line value \'''
2217
\''' # I guess this is a comment too
2221
m = sys.modules.get('__main__')
2222
globs = m.__dict__.copy()
2223
a = ConfigObj(testconfig1.split('\n'), raise_errors=True)
2224
b = ConfigObj(testconfig2.split('\n'), raise_errors=True)
2225
i = ConfigObj(testconfig6.split('\n'), raise_errors=True)
2227
'INTP_VER': INTP_VER,
2232
doctest.testmod(m, globs=globs)
2238
With list values off, ConfigObj can incorrectly unquote values. (This makes
2239
it impossible to use listquote to handle your list values for you - for
2240
nested lists. Not handling quotes at all would be better for this)
2245
A method to optionally remove uniform indentation from multiline values.
2246
(do as an example of using ``walk`` - along with string-escape)
2248
INCOMPATIBLE CHANGES
2249
====================
2251
(I have removed a lot of needless complications - this list is probably not
2252
conclusive, many option/attribute/method names have changed)
2256
The only valid divider is '='
2258
We've removed line continuations with '\'
2260
No recursive lists in values
2264
No distinction between flatfiles and non flatfiles
2266
Change in list syntax - use commas to indicate list, not parentheses
2267
(square brackets and parentheses are no longer recognised as lists)
2269
';' is no longer valid for comments and no multiline comments
2273
We don't allow empty values - have to use '' or ""
2275
In ConfigObj 3 - setting a non-flatfile member to ``None`` would
2276
initialise it as an empty section.
2278
The escape entities '&mjf-lf;' and '&mjf-quot;' have gone
2279
replaced by triple quote, multiple line values.
2281
The ``newline``, ``force_return``, and ``default`` options have gone
2283
The ``encoding`` and ``backup_encoding`` methods have gone - replaced
2284
with the ``encode`` and ``decode`` methods.
2286
``fileerror`` and ``createempty`` options have become ``file_error`` and
2289
Partial configspecs (for specifying the order members should be written
2290
out and which should be present) have gone. The configspec is no longer
2291
used to specify order for the ``write`` method.
2293
Exceeding the maximum depth of recursion in string interpolation now
2294
raises an error ``InterpolationDepthError``.
2296
Specifying a value for interpolation which doesn't exist now raises an
2297
error ``MissingInterpolationOption`` (instead of merely being ignored).
2299
The ``writein`` method has been removed.
2301
The comments attribute is now a list (``inline_comments`` equates to the
2302
old comments attribute)
2307
You can't have a keyword with the same name as a section (in the same
2308
section). They are both dictionary keys - so they would overlap.
2310
Interpolation checks first the 'DEFAULT' subsection of the current
2311
section, next it checks the 'DEFAULT' section of the parent section,
2312
last it checks the 'DEFAULT' section of the main section.
2314
Logically a 'DEFAULT' section should apply to all subsections of the *same
2315
parent* - this means that checking the 'DEFAULT' subsection in the
2316
*current section* is not necessarily logical ?
2318
In order to simplify unicode support (which is possibly of limited value
2319
in a config file) I have removed automatic support and added the
2320
``encode`` and ``decode methods, which can be used to transform keys and
2321
entries. Because the regex looks for specific values on inital parsing
2322
(i.e. the quotes and the equals signs) it can only read ascii compatible
2323
encodings. For unicode use ``UTF8``, which is ASCII compatible.
2325
Does it matter that we don't support the ':' divider, which is supported
2326
by ``ConfigParser`` ?
2328
Following error with "list_values=False" : ::
2330
>>> a = ["a='hello', 'goodbye'"]
2332
>>> c(a, list_values=False)
2333
{'a': "hello', 'goodbye"}
2335
The regular expression correctly removes the value -
2336
``"'hello', 'goodbye'"`` and then unquote just removes the front and
2337
back quotes (called from ``_handle_value``). What should we do ??
2338
(*ought* to raise exception because it's an invalid value if lists are
2339
off *sigh*. This is not what you want if you want to do your own list
2340
processing - would be *better* in this case not to unquote.)
2342
String interpolation and validation don't play well together. When
2343
validation changes type it sets the value. This will correctly fetch the
2344
value using interpolation - but then overwrite the interpolation reference.
2345
If the value is unchanged by validation (it's a string) - but other types
2351
List values allow you to specify multiple values for a keyword. This
2352
maps to a list as the resulting Python object when parsed.
2354
The syntax for lists is easy. A list is a comma separated set of values.
2355
If these values contain quotes, the hash mark, or commas, then the values
2356
can be surrounded by quotes. e.g. : ::
2358
keyword = value1, 'value 2', "value 3"
2360
If a value needs to be a list, but only has one member, then you indicate
2361
this with a trailing comma. e.g. : ::
2363
keyword = "single value",
2365
If a value needs to be a list, but it has no members, then you indicate
2366
this with a single comma. e.g. : ::
2368
keyword = , # an empty list
2370
Using triple quotes it will be possible for single values to contain
2371
newlines and *both* single quotes and double quotes. Triple quotes aren't
2372
allowed in list values. This means that the members of list values can't
2373
contain carriage returns (or line feeds :-) or both quote values.
2381
Fixed typo in ``write`` method. (Testing for the wrong value when resetting
2387
Fixed bug in ``setdefault`` - creating a new section *wouldn't* return
2388
a reference to the new section.
2393
Removed ``PositionError``.
2395
Allowed quotes around keys as documented.
2397
Fixed bug with commas in comments. (matched as a list value)
2404
Fixed bug in initialising ConfigObj from a ConfigObj.
2406
Changed the mailing list address.
2413
Fixed bug in ``Section__delitem__`` oops.
2418
Interpolation is switched off before writing out files.
2420
Fixed bug in handling ``StringIO`` instances. (Thanks to report from
2421
"Gustavo Niemeyer" <gustavo@niemeyer.net>)
2423
Moved the doctests from the ``__init__`` method to a separate function.
2424
(For the sake of IDE calltips).
2431
String values unchanged by validation *aren't* reset. This preserves
2432
interpolation in string values.
2437
None from a default is turned to '' if stringify is off - because setting
2438
a value to None raises an error.
2447
Actually added the RepeatSectionError class ;-)
2452
If ``stringify`` is off - list values are preserved by the ``validate``
2460
Fixed ``simpleVal``.
2462
Added ``RepeatSectionError`` error if you have additional sections in a
2463
section with a ``__many__`` (repeated) section.
2467
Reworked the ConfigObj._parse, _handle_error and _multiline methods:
2468
mutated the self._infile, self._index and self._maxline attributes into
2469
local variables and method parameters
2471
Reshaped the ConfigObj._multiline method to better reflect its semantics
2473
Changed the "default_test" test in ConfigObj.validate to check the fix for
2474
the bug in validate.Validator.check
2481
Updated comments at top
2488
Implemented repeated sections.
2492
Added test for interpreter version: raises RuntimeError if earlier than
2500
Implemented default values in configspecs.
2504
Fixed naked except: clause in validate that was silencing the fact
2505
that Python2.2 does not have dict.pop
2512
Bug fix causing error if file didn't exist.
2519
Adjusted doctests for Python 2.2.3 compatibility
2526
Added the inline_comments attribute
2528
We now preserve and rewrite all comments in the config file
2530
configspec is now a section attribute
2532
The validate method changes values in place
2534
Added InterpolationError
2536
The errors now have line number, line, and message attributes. This
2537
simplifies error handling
2546
Fixed bug in Section.pop (now doesn't raise KeyError if a default value
2549
Replaced ``basestring`` with ``types.StringTypes``
2551
Removed the ``writein`` method
2560
Indentation in config file is not significant anymore, subsections are
2561
designated by repeating square brackets
2563
Adapted all tests and docs to the new format
2577
Reformatted final docstring in ReST format, indented it for easier folding
2579
Code tests converted to doctest format, and scattered them around
2580
in various docstrings
2582
Walk method rewritten using scalars and sections attributes
2589
Changed Validator and SimpleVal "test" methods to "check"
2596
Changed Section.sequence to Section.scalars and Section.sections
2598
Added Section.configspec
2600
Sections in the root section now have no extra indentation
2602
Comments now better supported in Section and preserved by ConfigObj
2604
Comments also written out
2606
Implemented initial_comment and final_comment
2608
A scalar value after a section will now raise an error
2613
Fixed a couple of bugs
2615
Can now pass a tuple instead of a list
2617
Simplified dict and walk methods
2619
Added __str__ to Section
2631
The stringify option implemented. On by default.
2636
Renamed private attributes with a single underscore prefix.
2638
Changes to interpolation - exceeding recursion depth, or specifying a
2639
missing value, now raise errors.
2641
Changes for Python 2.2 compatibility. (changed boolean tests - removed
2642
``is True`` and ``is False``)
2644
Added test for duplicate section and member (and fixed bug)
2658
Now properly handles values including comments and lists.
2660
Better error handling.
2662
String interpolation.
2664
Some options implemented.
2666
You can pass a Section a dictionary to initialise it.
2668
Setting a Section member to a dictionary will create a Section instance.
2675
Experimental reader.
2677
A reasonably elegant implementation - a basic reader in 160 lines of code.
2679
*A programming language is a medium of expression.* - Paul Graham
2335
# Check / processing functions for options
2336
def flatten_errors(cfg, res, levels=None, results=None):
2338
An example function that will turn a nested dictionary of results
2339
(as returned by ``ConfigObj.validate``) into a flat list.
2341
``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2342
dictionary returned by ``validate``.
2344
(This is a recursive function, so you shouldn't use the ``levels`` or
2345
``results`` arguments - they are used by the function.)
2347
Returns a list of keys that failed. Each member of the list is a tuple :
2351
([list of sections...], key, result)
2353
If ``validate`` was called with ``preserve_errors=False`` (the default)
2354
then ``result`` will always be ``False``.
2356
*list of sections* is a flattened list of sections that the key was found
2359
If the section was missing (or a section was expected and a scalar provided
2360
- or vice-versa) then key will be ``None``.
2362
If the value (or section) was missing then ``result`` will be ``False``.
2364
If ``validate`` was called with ``preserve_errors=True`` and a value
2365
was present, but failed the check, then ``result`` will be the exception
2366
object returned. You can use this as a string that describes the failure.
2368
For example *The value "3" is of the wrong type*.
2371
>>> vtor = validate.Validator()
2377
... another_option = Probably
2379
... another_option = True
2386
... option1 = boolean()
2387
... option2 = boolean()
2388
... option3 = boolean(default=Bad_value)
2390
... option1 = boolean()
2391
... option2 = boolean()
2392
... option3 = boolean(default=Bad_value)
2394
... another_option = boolean()
2396
... another_option = boolean()
2399
... value2 = integer
2400
... value3 = integer(0, 10)
2401
... [[[section3b-sub]]]
2404
... another_option = boolean()
2406
>>> cs = my_cfg.split('\\n')
2407
>>> ini = my_ini.split('\\n')
2408
>>> cfg = ConfigObj(ini, configspec=cs)
2409
>>> res = cfg.validate(vtor, preserve_errors=True)
2411
>>> for entry in flatten_errors(cfg, res):
2412
... section_list, key, error = entry
2413
... section_list.insert(0, '[root]')
2414
... if key is not None:
2415
... section_list.append(key)
2417
... section_list.append('[missing]')
2418
... section_string = ', '.join(section_list)
2419
... errors.append((section_string, ' = ', error))
2421
>>> for entry in errors:
2422
... print entry[0], entry[1], (entry[2] or 0)
2424
[root], option3 = the value "Bad_value" is of the wrong type.
2425
[root], section1, option2 = 0
2426
[root], section1, option3 = the value "Bad_value" is of the wrong type.
2427
[root], section2, another_option = the value "Probably" is of the wrong type.
2428
[root], section3, section3b, section3b-sub, [missing] = 0
2429
[root], section3, section3b, value2 = the value "a" is of the wrong type.
2430
[root], section3, section3b, value3 = the value "11" is too big.
2431
[root], section4, [missing] = 0
2439
if res is False or isinstance(res, Exception):
2440
results.append((levels[:], None, res))
2444
for (key, val) in res.items():
2447
if isinstance(cfg.get(key), dict):
2450
flatten_errors(cfg[key], val, levels, results)
2452
results.append((levels[:], key, val))
2461
"""*A programming language is a medium of expression.* - Paul Graham"""