2
# A config file reader/writer that supports nested sections in config files.
3
# Copyright (C) 2005-2008 Michael Foord, Nicola Larosa
4
# E-mail: fuzzyman AT voidspace DOT org DOT uk
5
# nico AT tekNico DOT net
8
# http://www.voidspace.org.uk/python/configobj.html
10
# Released subject to the BSD License
11
# Please see http://www.voidspace.org.uk/python/license.shtml
13
# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
14
# For information about bugfixes, updates and support, please join the
15
# ConfigObj mailing list:
16
# http://lists.sourceforge.net/lists/listinfo/configobj-develop
17
# Comments, suggestions and bug reports welcome.
19
from __future__ import generators
22
INTP_VER = sys.version_info[:2]
24
raise RuntimeError("Python v.2.2 or later needed")
28
from types import StringTypes
29
from warnings import warn
31
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
33
# Python 2.2 does not have these
35
BOM_UTF8 = '\xef\xbb\xbf'
36
# UTF-16, little endian
37
BOM_UTF16_LE = '\xff\xfe'
39
BOM_UTF16_BE = '\xfe\xff'
40
if sys.byteorder == 'little':
41
# UTF-16, native endianness
42
BOM_UTF16 = BOM_UTF16_LE
44
# UTF-16, native endianness
45
BOM_UTF16 = BOM_UTF16_BE
47
# A dictionary mapping BOM to
48
# the encoding to decode with, and what to set the
49
# encoding attribute to.
51
BOM_UTF8: ('utf_8', None),
52
BOM_UTF16_BE: ('utf16_be', 'utf_16'),
53
BOM_UTF16_LE: ('utf16_le', 'utf_16'),
54
BOM_UTF16: ('utf_16', 'utf_16'),
56
# All legal variants of the BOM codecs.
57
# TODO: the list of aliases is not meant to be exhaustive, is there a
64
'utf16_be': 'utf16_be',
65
'utf_16_be': 'utf16_be',
66
'utf-16be': 'utf16_be',
67
'utf16_le': 'utf16_le',
68
'utf_16_le': 'utf16_le',
69
'utf-16le': 'utf16_le',
77
# Map of encodings to the BOM to write.
81
'utf16_be': BOM_UTF16_BE,
82
'utf16_le': BOM_UTF16_LE,
87
def match_utf8(encoding):
88
return BOM_LIST.get(encoding.lower()) == 'utf_8'
91
# Quote strings used for writing values
95
wspace_plus = ' \r\t\n\v\t\'"'
103
"""enumerate for Python 2.2."""
115
__version__ = '4.5.2'
117
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
119
__docformat__ = "restructuredtext en"
123
'DEFAULT_INDENT_TYPE',
124
'DEFAULT_INTERPOLATION',
132
'InterpolationError',
133
'InterpolationLoopError',
134
'MissingInterpolationOption',
135
'RepeatSectionError',
143
DEFAULT_INTERPOLATION = 'configparser'
144
DEFAULT_INDENT_TYPE = ' '
145
MAX_INTERPOL_DEPTH = 10
148
'interpolation': True,
149
'raise_errors': False,
151
'create_empty': False,
155
# option may be set to one of ('', ' ', '\t')
158
'default_encoding': None,
160
'write_empty_values': False,
168
raise ImportError('compiler module not available')
169
p = compiler.parse(s)
170
return p.getChildren()[1].getChildren()[0].getChildren()[1]
173
class UnknownType(Exception):
177
class Builder(object):
180
m = getattr(self, 'build_' + o.__class__.__name__, None)
182
raise UnknownType(o.__class__.__name__)
185
def build_List(self, o):
186
return map(self.build, o.getChildren())
188
def build_Const(self, o):
191
def build_Dict(self, o):
193
i = iter(map(self.build, o.getChildren()))
198
def build_Tuple(self, o):
199
return tuple(self.build_List(o))
201
def build_Name(self, o):
206
if o.name == 'False':
210
raise UnknownType('Undefined Name')
212
def build_Add(self, o):
213
real, imag = map(self.build_Const, o.getChildren())
217
raise UnknownType('Add')
218
if not isinstance(imag, complex) or imag.real != 0.0:
219
raise UnknownType('Add')
222
def build_Getattr(self, o):
223
parent = self.build(o.expr)
224
return getattr(parent, o.attrname)
226
def build_UnarySub(self, o):
227
return -self.build_Const(o.getChildren()[0])
229
def build_UnaryAdd(self, o):
230
return self.build_Const(o.getChildren()[0])
239
return _builder.build(getObj(s))
243
class ConfigObjError(SyntaxError):
245
This is the base class for all errors that ConfigObj raises.
246
It is a subclass of SyntaxError.
248
def __init__(self, message='', line_number=None, line=''):
250
self.line_number = line_number
251
self.message = message
252
SyntaxError.__init__(self, message)
255
class NestingError(ConfigObjError):
257
This error indicates a level of nesting that doesn't match.
261
class ParseError(ConfigObjError):
263
This error indicates that a line is badly written.
264
It is neither a valid ``key = value`` line,
265
nor a valid section marker line.
269
class ReloadError(IOError):
271
A 'reload' operation failed.
272
This exception is a subclass of ``IOError``.
275
IOError.__init__(self, 'reload failed, filename is not set.')
278
class DuplicateError(ConfigObjError):
280
The keyword or section specified already exists.
284
class ConfigspecError(ConfigObjError):
286
An error occured whilst parsing a configspec.
290
class InterpolationError(ConfigObjError):
291
"""Base class for the two interpolation errors."""
294
class InterpolationLoopError(InterpolationError):
295
"""Maximum interpolation depth exceeded in string interpolation."""
297
def __init__(self, option):
298
InterpolationError.__init__(
300
'interpolation loop detected in value "%s".' % option)
303
class RepeatSectionError(ConfigObjError):
305
This error indicates additional sections in a section with a
306
``__many__`` (repeated) section.
310
class MissingInterpolationOption(InterpolationError):
311
"""A value specified for interpolation was missing."""
313
def __init__(self, option):
314
InterpolationError.__init__(
316
'missing option "%s" in interpolation.' % option)
319
class UnreprError(ConfigObjError):
320
"""An error parsing in unrepr mode."""
324
class InterpolationEngine(object):
326
A helper class to help perform string interpolation.
328
This class is an abstract base class; its descendants perform
332
# compiled regexp to use in self.interpolate()
333
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
335
def __init__(self, section):
336
# the Section instance that "owns" this engine
337
self.section = section
340
def interpolate(self, key, value):
341
def recursive_interpolate(key, value, section, backtrail):
342
"""The function that does the actual work.
344
``value``: the string we're trying to interpolate.
345
``section``: the section in which that string was found
346
``backtrail``: a dict to keep track of where we've been,
347
to detect and prevent infinite recursion loops
349
This is similar to a depth-first-search algorithm.
351
# Have we been here already?
352
if backtrail.has_key((key, section.name)):
353
# Yes - infinite loop detected
354
raise InterpolationLoopError(key)
355
# Place a marker on our backtrail so we won't come back here again
356
backtrail[(key, section.name)] = 1
358
# Now start the actual work
359
match = self._KEYCRE.search(value)
361
# The actual parsing of the match is implementation-dependent,
362
# so delegate to our helper function
363
k, v, s = self._parse_match(match)
365
# That's the signal that no further interpolation is needed
368
# Further interpolation may be needed to obtain final value
369
replacement = recursive_interpolate(k, v, s, backtrail)
370
# Replace the matched string with its final value
371
start, end = match.span()
372
value = ''.join((value[:start], replacement, value[end:]))
373
new_search_start = start + len(replacement)
374
# Pick up the next interpolation key, if any, for next time
375
# through the while loop
376
match = self._KEYCRE.search(value, new_search_start)
378
# Now safe to come back here again; remove marker from backtrail
379
del backtrail[(key, section.name)]
383
# Back in interpolate(), all we have to do is kick off the recursive
384
# function with appropriate starting values
385
value = recursive_interpolate(key, value, self.section, {})
389
def _fetch(self, key):
390
"""Helper function to fetch values from owning section.
392
Returns a 2-tuple: the value, and the section where it was found.
394
# switch off interpolation before we try and fetch anything !
395
save_interp = self.section.main.interpolation
396
self.section.main.interpolation = False
398
# Start at section that "owns" this InterpolationEngine
399
current_section = self.section
401
# try the current section first
402
val = current_section.get(key)
406
val = current_section.get('DEFAULT', {}).get(key)
409
# move up to parent and try again
410
# top-level's parent is itself
411
if current_section.parent is current_section:
412
# reached top level, time to give up
414
current_section = current_section.parent
416
# restore interpolation to previous value before returning
417
self.section.main.interpolation = save_interp
419
raise MissingInterpolationOption(key)
420
return val, current_section
423
def _parse_match(self, match):
424
"""Implementation-dependent helper function.
426
Will be passed a match object corresponding to the interpolation
427
key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
428
key in the appropriate config file section (using the ``_fetch()``
429
helper function) and return a 3-tuple: (key, value, section)
431
``key`` is the name of the key we're looking for
432
``value`` is the value found for that key
433
``section`` is a reference to the section where it was found
435
``key`` and ``section`` should be None if no further
436
interpolation should be performed on the resulting value
437
(e.g., if we interpolated "$$" and returned "$").
439
raise NotImplementedError()
443
class ConfigParserInterpolation(InterpolationEngine):
444
"""Behaves like ConfigParser."""
445
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
447
def _parse_match(self, match):
449
value, section = self._fetch(key)
450
return key, value, section
454
class TemplateInterpolation(InterpolationEngine):
455
"""Behaves like string.Template."""
457
_KEYCRE = re.compile(r"""
459
(?P<escaped>\$) | # Two $ signs
460
(?P<named>[_a-z][_a-z0-9]*) | # $name format
461
{(?P<braced>[^}]*)} # ${name} format
463
""", re.IGNORECASE | re.VERBOSE)
465
def _parse_match(self, match):
466
# Valid name (in or out of braces): fetch value from section
467
key = match.group('named') or match.group('braced')
469
value, section = self._fetch(key)
470
return key, value, section
471
# Escaped delimiter (e.g., $$): return single delimiter
472
if match.group('escaped') is not None:
473
# Return None for key and section to indicate it's time to stop
474
return None, self._delimiter, None
475
# Anything else: ignore completely, just return it unchanged
476
return None, match.group(), None
479
interpolation_engines = {
480
'configparser': ConfigParserInterpolation,
481
'template': TemplateInterpolation,
488
A dictionary-like object that represents a section in a config file.
490
It does string interpolation if the 'interpolation' attribute
491
of the 'main' object is set to True.
493
Interpolation is tried first from this object, then from the 'DEFAULT'
494
section of this object, next from the parent and its 'DEFAULT' section,
495
and so on until the main object is reached.
497
A Section will behave like an ordered dictionary - following the
498
order of the ``scalars`` and ``sections`` attributes.
499
You can use this to change the order of members.
501
Iteration follows the order: scalars, then sections.
504
def __init__(self, parent, depth, main, indict=None, name=None):
506
* parent is the section above
507
* depth is the depth level of this section
508
* main is the main ConfigObj
509
* indict is a dictionary to initialise the section with
514
# used for nesting level *and* interpolation
516
# used for the interpolation attribute
518
# level of nesting depth of this Section
520
# purely for information
524
# we do this explicitly so that __setitem__ is used properly
525
# (rather than just passing to ``dict.__init__``)
526
for entry, value in indict.iteritems():
530
def _initialise(self):
531
# the sequence of scalar values in this Section
533
# the sequence of sections in this Section
537
self.inline_comments = {}
541
self._configspec_comments = {}
542
self._configspec_inline_comments = {}
543
self._cs_section_comments = {}
544
self._cs_section_inline_comments = {}
547
self.default_values = {}
550
def _interpolate(self, key, value):
552
# do we already have an interpolation engine?
553
engine = self._interpolation_engine
554
except AttributeError:
555
# not yet: first time running _interpolate(), so pick the engine
556
name = self.main.interpolation
557
if name == True: # note that "if name:" would be incorrect here
558
# backwards-compatibility: interpolation=True means use default
559
name = DEFAULT_INTERPOLATION
560
name = name.lower() # so that "Template", "template", etc. all work
561
class_ = interpolation_engines.get(name, None)
563
# invalid value for self.main.interpolation
564
self.main.interpolation = False
567
# save reference to engine so we don't have to do this again
568
engine = self._interpolation_engine = class_(self)
569
# let the engine do the actual work
570
return engine.interpolate(key, value)
573
def __getitem__(self, key):
574
"""Fetch the item and do string interpolation."""
575
val = dict.__getitem__(self, key)
576
if self.main.interpolation and isinstance(val, StringTypes):
577
return self._interpolate(key, val)
581
def __setitem__(self, key, value, unrepr=False):
583
Correctly set a value.
585
Making dictionary values Section instances.
586
(We have to special case 'Section' instances - which are also dicts)
588
Keys must be strings.
589
Values need only be strings (or lists of strings) if
590
``main.stringify`` is set.
592
`unrepr`` must be set when setting a value to a dictionary, without
593
creating a new sub-section.
595
if not isinstance(key, StringTypes):
596
raise ValueError('The key "%s" is not a string.' % key)
599
if not self.comments.has_key(key):
600
self.comments[key] = []
601
self.inline_comments[key] = ''
602
# remove the entry from defaults
603
if key in self.defaults:
604
self.defaults.remove(key)
606
if isinstance(value, Section):
607
if not self.has_key(key):
608
self.sections.append(key)
609
dict.__setitem__(self, key, value)
610
elif isinstance(value, dict) and not unrepr:
611
# First create the new depth level,
612
# then create the section
613
if not self.has_key(key):
614
self.sections.append(key)
615
new_depth = self.depth + 1
626
if not self.has_key(key):
627
self.scalars.append(key)
628
if not self.main.stringify:
629
if isinstance(value, StringTypes):
631
elif isinstance(value, (list, tuple)):
633
if not isinstance(entry, StringTypes):
634
raise TypeError('Value is not a string "%s".' % entry)
636
raise TypeError('Value is not a string "%s".' % value)
637
dict.__setitem__(self, key, value)
640
def __delitem__(self, key):
641
"""Remove items from the sequence when deleting."""
642
dict. __delitem__(self, key)
643
if key in self.scalars:
644
self.scalars.remove(key)
646
self.sections.remove(key)
647
del self.comments[key]
648
del self.inline_comments[key]
651
def get(self, key, default=None):
652
"""A version of ``get`` that doesn't bypass string interpolation."""
659
def update(self, indict):
661
A version of update that uses our ``__setitem__``.
664
self[entry] = indict[entry]
667
def pop(self, key, *args):
669
'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
670
If key is not found, d is returned if given, otherwise KeyError is raised'
672
val = dict.pop(self, key, *args)
673
if key in self.scalars:
674
del self.comments[key]
675
del self.inline_comments[key]
676
self.scalars.remove(key)
677
elif key in self.sections:
678
del self.comments[key]
679
del self.inline_comments[key]
680
self.sections.remove(key)
681
if self.main.interpolation and isinstance(val, StringTypes):
682
return self._interpolate(key, val)
687
"""Pops the first (key,val)"""
688
sequence = (self.scalars + self.sections)
690
raise KeyError(": 'popitem(): dictionary is empty'")
699
A version of clear that also affects scalars/sections
700
Also clears comments and configspec.
702
Leaves other attributes alone :
703
depth/main/parent are not affected
709
self.inline_comments = {}
713
def setdefault(self, key, default=None):
714
"""A version of setdefault that sets sequence if appropriate."""
723
"""D.items() -> list of D's (key, value) pairs, as 2-tuples"""
724
return zip((self.scalars + self.sections), self.values())
728
"""D.keys() -> list of D's keys"""
729
return (self.scalars + self.sections)
733
"""D.values() -> list of D's values"""
734
return [self[key] for key in (self.scalars + self.sections)]
738
"""D.iteritems() -> an iterator over the (key, value) items of D"""
739
return iter(self.items())
743
"""D.iterkeys() -> an iterator over the keys of D"""
744
return iter((self.scalars + self.sections))
749
def itervalues(self):
750
"""D.itervalues() -> an iterator over the values of D"""
751
return iter(self.values())
755
"""x.__repr__() <==> repr(x)"""
756
return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
757
for key in (self.scalars + self.sections)])
760
__str__.__doc__ = "x.__str__() <==> str(x)"
763
# Extra methods - not in a normal dictionary
767
Return a deepcopy of self as a dictionary.
769
All members that are ``Section`` instances are recursively turned to
770
ordinary dictionaries - by calling their ``dict`` method.
780
this_entry = self[entry]
781
if isinstance(this_entry, Section):
782
this_entry = this_entry.dict()
783
elif isinstance(this_entry, list):
784
# create a copy rather than a reference
785
this_entry = list(this_entry)
786
elif isinstance(this_entry, tuple):
787
# create a copy rather than a reference
788
this_entry = tuple(this_entry)
789
newdict[entry] = this_entry
793
def merge(self, indict):
795
A recursive update - useful for merging config files.
797
>>> a = '''[section1]
800
... more_options = False
801
... # end of file'''.splitlines()
802
>>> b = '''# File is user.ini
805
... # end of file'''.splitlines()
806
>>> c1 = ConfigObj(b)
807
>>> c2 = ConfigObj(a)
810
{'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
812
for key, val in indict.items():
813
if (key in self and isinstance(self[key], dict) and
814
isinstance(val, dict)):
820
def rename(self, oldkey, newkey):
822
Change a keyname to another, without changing position in sequence.
824
Implemented so that transformations can be made on keys,
825
as well as on values. (used by encode and decode)
827
Also renames comments.
829
if oldkey in self.scalars:
830
the_list = self.scalars
831
elif oldkey in self.sections:
832
the_list = self.sections
834
raise KeyError('Key "%s" not found.' % oldkey)
835
pos = the_list.index(oldkey)
838
dict.__delitem__(self, oldkey)
839
dict.__setitem__(self, newkey, val)
840
the_list.remove(oldkey)
841
the_list.insert(pos, newkey)
842
comm = self.comments[oldkey]
843
inline_comment = self.inline_comments[oldkey]
844
del self.comments[oldkey]
845
del self.inline_comments[oldkey]
846
self.comments[newkey] = comm
847
self.inline_comments[newkey] = inline_comment
850
def walk(self, function, raise_errors=True,
851
call_on_sections=False, **keywargs):
853
Walk every member and call a function on the keyword and value.
855
Return a dictionary of the return values
857
If the function raises an exception, raise the errror
858
unless ``raise_errors=False``, in which case set the return value to
861
Any unrecognised keyword arguments you pass to walk, will be pased on
862
to the function you pass in.
864
Note: if ``call_on_sections`` is ``True`` then - on encountering a
865
subsection, *first* the function is called for the *whole* subsection,
866
and then recurses into it's members. This means your function must be
867
able to handle strings, dictionaries and lists. This allows you
868
to change the key of subsections as well as for ordinary members. The
869
return value when called on the whole subsection has to be discarded.
871
See the encode and decode methods for examples, including functions.
875
You can use ``walk`` to transform the names of members of a section
876
but you mustn't add or delete members.
878
>>> config = '''[XXXXsection]
879
... XXXXkey = XXXXvalue'''.splitlines()
880
>>> cfg = ConfigObj(config)
882
{'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
883
>>> def transform(section, key):
884
... val = section[key]
885
... newkey = key.replace('XXXX', 'CLIENT1')
886
... section.rename(key, newkey)
887
... if isinstance(val, (tuple, list, dict)):
890
... val = val.replace('XXXX', 'CLIENT1')
891
... section[newkey] = val
892
>>> cfg.walk(transform, call_on_sections=True)
893
{'CLIENT1section': {'CLIENT1key': None}}
895
{'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
899
for i in range(len(self.scalars)):
900
entry = self.scalars[i]
902
val = function(self, entry, **keywargs)
903
# bound again in case name has changed
904
entry = self.scalars[i]
910
entry = self.scalars[i]
913
for i in range(len(self.sections)):
914
entry = self.sections[i]
917
function(self, entry, **keywargs)
922
entry = self.sections[i]
924
# bound again in case name has changed
925
entry = self.sections[i]
926
# previous result is discarded
927
out[entry] = self[entry].walk(
929
raise_errors=raise_errors,
930
call_on_sections=call_on_sections,
935
def decode(self, encoding):
937
Decode all strings and values to unicode, using the specified encoding.
939
Works with subsections and list values.
941
Uses the ``walk`` method.
943
Testing ``encode`` and ``decode``.
945
>>> m.decode('ascii')
946
>>> def testuni(val):
947
... for entry in val:
948
... if not isinstance(entry, unicode):
949
... print >> sys.stderr, type(entry)
950
... raise AssertionError, 'decode failed.'
951
... if isinstance(val[entry], dict):
952
... testuni(val[entry])
953
... elif not isinstance(val[entry], unicode):
954
... raise AssertionError, 'decode failed.'
956
>>> m.encode('ascii')
960
warn('use of ``decode`` is deprecated.', DeprecationWarning)
961
def decode(section, key, encoding=encoding, warn=True):
964
if isinstance(val, (list, tuple)):
967
newval.append(entry.decode(encoding))
968
elif isinstance(val, dict):
971
newval = val.decode(encoding)
972
newkey = key.decode(encoding)
973
section.rename(key, newkey)
974
section[newkey] = newval
975
# using ``call_on_sections`` allows us to modify section names
976
self.walk(decode, call_on_sections=True)
979
def encode(self, encoding):
981
Encode all strings and values from unicode,
982
using the specified encoding.
984
Works with subsections and list values.
985
Uses the ``walk`` method.
987
warn('use of ``encode`` is deprecated.', DeprecationWarning)
988
def encode(section, key, encoding=encoding):
991
if isinstance(val, (list, tuple)):
994
newval.append(entry.encode(encoding))
995
elif isinstance(val, dict):
998
newval = val.encode(encoding)
999
newkey = key.encode(encoding)
1000
section.rename(key, newkey)
1001
section[newkey] = newval
1002
self.walk(encode, call_on_sections=True)
1005
def istrue(self, key):
1006
"""A deprecated version of ``as_bool``."""
1007
warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
1008
'instead.', DeprecationWarning)
1009
return self.as_bool(key)
1012
def as_bool(self, key):
1014
Accepts a key as input. The corresponding value must be a string or
1015
the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
1016
retain compatibility with Python 2.2.
1018
If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
1021
If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
1024
``as_bool`` is not case sensitive.
1026
Any other input will raise a ``ValueError``.
1031
Traceback (most recent call last):
1032
ValueError: Value "fish" is neither True nor False
1047
if not isinstance(val, StringTypes):
1048
# TODO: Why do we raise a KeyError here?
1051
return self.main._bools[val.lower()]
1053
raise ValueError('Value "%s" is neither True nor False' % val)
1056
def as_int(self, key):
1058
A convenience method which coerces the specified value to an integer.
1060
If the value is an invalid literal for ``int``, a ``ValueError`` will
1066
Traceback (most recent call last):
1067
ValueError: invalid literal for int(): fish
1073
Traceback (most recent call last):
1074
ValueError: invalid literal for int(): 3.2
1076
return int(self[key])
1079
def as_float(self, key):
1081
A convenience method which coerces the specified value to a float.
1083
If the value is an invalid literal for ``float``, a ``ValueError`` will
1089
Traceback (most recent call last):
1090
ValueError: invalid literal for float(): fish
1098
return float(self[key])
1101
def restore_default(self, key):
1103
Restore (and return) default value for the specified key.
1105
This method will only work for a ConfigObj that was created
1106
with a configspec and has been validated.
1108
If there is no default value for this key, ``KeyError`` is raised.
1110
default = self.default_values[key]
1111
dict.__setitem__(self, key, default)
1112
if key not in self.defaults:
1113
self.defaults.append(key)
1117
def restore_defaults(self):
1119
Recursively restore default values to all members
1122
This method will only work for a ConfigObj that was created
1123
with a configspec and has been validated.
1125
It doesn't delete or modify entries without default values.
1127
for key in self.default_values:
1128
self.restore_default(key)
1130
for section in self.sections:
1131
self[section].restore_defaults()
1134
class ConfigObj(Section):
1135
"""An object to read, create, and write config files."""
1137
_keyword = re.compile(r'''^ # line start
1140
(?:".*?")| # double quotes
1141
(?:'.*?')| # single quotes
1142
(?:[^'"=].*?) # no quotes
1145
(.*) # value (including list values and comments)
1150
_sectionmarker = re.compile(r'''^
1151
(\s*) # 1: indentation
1152
((?:\[\s*)+) # 2: section marker open
1153
( # 3: section name open
1154
(?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1155
(?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1156
(?:[^'"\s].*?) # at least one non-space unquoted
1157
) # section name close
1158
((?:\s*\])+) # 4: section marker close
1159
\s*(\#.*)? # 5: optional comment
1163
# this regexp pulls list values out as a single string
1164
# or single values and comments
1165
# FIXME: this regex adds a '' to the end of comma terminated lists
1166
# workaround in ``_handle_value``
1167
_valueexp = re.compile(r'''^
1173
(?:".*?")| # double quotes
1174
(?:'.*?')| # single quotes
1175
(?:[^'",\#][^,\#]*?) # unquoted
1178
)* # match all list items ending in a comma (if any)
1181
(?:".*?")| # double quotes
1182
(?:'.*?')| # single quotes
1183
(?:[^'",\#\s][^,]*?)| # unquoted
1184
(?:(?<!,)) # Empty value
1185
)? # last item in a list - or string value
1187
(,) # alternatively a single comma - empty list
1189
\s*(\#.*)? # optional comment
1193
# use findall to get the members of a list value
1194
_listvalueexp = re.compile(r'''
1196
(?:".*?")| # double quotes
1197
(?:'.*?')| # single quotes
1198
(?:[^'",\#].*?) # unquoted
1204
# this regexp is used for the value
1205
# when lists are switched off
1206
_nolistvalue = re.compile(r'''^
1208
(?:".*?")| # double quotes
1209
(?:'.*?')| # single quotes
1210
(?:[^'"\#].*?)| # unquoted
1213
\s*(\#.*)? # optional comment
1217
# regexes for finding triple quoted values on one line
1218
_single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1219
_single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1220
_multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1221
_multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1224
"'''": (_single_line_single, _multi_line_single),
1225
'"""': (_single_line_double, _multi_line_double),
1228
# Used by the ``istrue`` Section method
1230
'yes': True, 'no': False,
1231
'on': True, 'off': False,
1232
'1': True, '0': False,
1233
'true': True, 'false': False,
1237
def __init__(self, infile=None, options=None, **kwargs):
1239
Parse a config file or create a config file object.
1241
``ConfigObj(infile=None, options=None, **kwargs)``
1243
# init the superclass
1244
Section.__init__(self, self, 0, self)
1251
options = dict(options)
1253
# keyword arguments take precedence over an options dictionary
1254
options.update(kwargs)
1256
defaults = OPTION_DEFAULTS.copy()
1257
# TODO: check the values too.
1258
for entry in options:
1259
if entry not in defaults:
1260
raise TypeError('Unrecognised option "%s".' % entry)
1262
# Add any explicit options to the defaults
1263
defaults.update(options)
1264
self._initialise(defaults)
1265
configspec = defaults['configspec']
1266
self._original_configspec = configspec
1267
self._load(infile, configspec)
1270
def _load(self, infile, configspec):
1271
if isinstance(infile, StringTypes):
1272
self.filename = infile
1273
if os.path.isfile(infile):
1274
h = open(infile, 'rb')
1275
infile = h.read() or []
1277
elif self.file_error:
1278
# raise an error if the file doesn't exist
1279
raise IOError('Config file not found: "%s".' % self.filename)
1281
# file doesn't already exist
1282
if self.create_empty:
1283
# this is a good test that the filename specified
1284
# isn't impossible - like on a non-existent device
1285
h = open(infile, 'w')
1290
elif isinstance(infile, (list, tuple)):
1291
infile = list(infile)
1293
elif isinstance(infile, dict):
1295
# the Section class handles creating subsections
1296
if isinstance(infile, ConfigObj):
1297
# get a copy of our ConfigObj
1298
infile = infile.dict()
1300
for entry in infile:
1301
self[entry] = infile[entry]
1304
if configspec is not None:
1305
self._handle_configspec(configspec)
1307
self.configspec = None
1310
elif getattr(infile, 'read', None) is not None:
1311
# This supports file like objects
1312
infile = infile.read() or []
1313
# needs splitting into lines - but needs doing *after* decoding
1314
# in case it's not an 8 bit encoding
1316
raise TypeError('infile must be a filename, file like object, or list of lines.')
1319
# don't do it for the empty ConfigObj
1320
infile = self._handle_bom(infile)
1321
# infile is now *always* a list
1323
# Set the newlines attribute (first line ending it finds)
1324
# and strip trailing '\n' or '\r' from lines
1326
if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1328
for end in ('\r\n', '\n', '\r'):
1329
if line.endswith(end):
1334
infile = [line.rstrip('\r\n') for line in infile]
1337
# if we had any errors, now is the time to raise them
1339
info = "at line %s." % self._errors[0].line_number
1340
if len(self._errors) > 1:
1341
msg = "Parsing failed with several errors.\nFirst error %s" % info
1342
error = ConfigObjError(msg)
1344
error = self._errors[0]
1345
# set the errors attribute; it's a list of tuples:
1346
# (error_type, message, line_number)
1347
error.errors = self._errors
1348
# set the config attribute
1351
# delete private attributes
1354
if configspec is None:
1355
self.configspec = None
1357
self._handle_configspec(configspec)
1360
def _initialise(self, options=None):
1362
options = OPTION_DEFAULTS
1364
# initialise a few variables
1365
self.filename = None
1367
self.raise_errors = options['raise_errors']
1368
self.interpolation = options['interpolation']
1369
self.list_values = options['list_values']
1370
self.create_empty = options['create_empty']
1371
self.file_error = options['file_error']
1372
self.stringify = options['stringify']
1373
self.indent_type = options['indent_type']
1374
self.encoding = options['encoding']
1375
self.default_encoding = options['default_encoding']
1377
self.newlines = None
1378
self.write_empty_values = options['write_empty_values']
1379
self.unrepr = options['unrepr']
1381
self.initial_comment = []
1382
self.final_comment = []
1383
self.configspec = {}
1385
# Clear section attributes as well
1386
Section._initialise(self)
1390
return ('ConfigObj({%s})' %
1391
', '.join([('%s: %s' % (repr(key), repr(self[key])))
1392
for key in (self.scalars + self.sections)]))
1395
def _handle_bom(self, infile):
1397
Handle any BOM, and decode if necessary.
1399
If an encoding is specified, that *must* be used - but the BOM should
1400
still be removed (and the BOM attribute set).
1402
(If the encoding is wrongly specified, then a BOM for an alternative
1403
encoding won't be discovered or removed.)
1405
If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1406
removed. The BOM attribute will be set. UTF16 will be decoded to
1409
NOTE: This method must not be called with an empty ``infile``.
1411
Specifying the *wrong* encoding is likely to cause a
1412
``UnicodeDecodeError``.
1414
``infile`` must always be returned as a list of lines, but may be
1415
passed in as a single string.
1417
if ((self.encoding is not None) and
1418
(self.encoding.lower() not in BOM_LIST)):
1419
# No need to check for a BOM
1420
# the encoding specified doesn't have one
1422
return self._decode(infile, self.encoding)
1424
if isinstance(infile, (list, tuple)):
1428
if self.encoding is not None:
1429
# encoding explicitly supplied
1430
# And it could have an associated BOM
1431
# TODO: if encoding is just UTF16 - we ought to check for both
1432
# TODO: big endian and little endian versions.
1433
enc = BOM_LIST[self.encoding.lower()]
1435
# For UTF16 we try big endian and little endian
1436
for BOM, (encoding, final_encoding) in BOMS.items():
1437
if not final_encoding:
1440
if infile.startswith(BOM):
1443
# Don't need to remove BOM
1444
return self._decode(infile, encoding)
1446
# If we get this far, will *probably* raise a DecodeError
1447
# As it doesn't appear to start with a BOM
1448
return self._decode(infile, self.encoding)
1452
if not line.startswith(BOM):
1453
return self._decode(infile, self.encoding)
1455
newline = line[len(BOM):]
1458
if isinstance(infile, (list, tuple)):
1463
return self._decode(infile, self.encoding)
1465
# No encoding specified - so we need to check for UTF8/UTF16
1466
for BOM, (encoding, final_encoding) in BOMS.items():
1467
if not line.startswith(BOM):
1471
self.encoding = final_encoding
1472
if not final_encoding:
1476
newline = line[len(BOM):]
1477
if isinstance(infile, (list, tuple)):
1481
# UTF8 - don't decode
1482
if isinstance(infile, StringTypes):
1483
return infile.splitlines(True)
1486
# UTF16 - have to decode
1487
return self._decode(infile, encoding)
1489
# No BOM discovered and no encoding specified, just return
1490
if isinstance(infile, StringTypes):
1491
# infile read from a file will be a single string
1492
return infile.splitlines(True)
1496
def _a_to_u(self, aString):
1497
"""Decode ASCII strings to unicode if a self.encoding is specified."""
1499
return aString.decode('ascii')
1504
def _decode(self, infile, encoding):
1506
Decode infile to unicode. Using the specified encoding.
1508
if is a string, it also needs converting to a list.
1510
if isinstance(infile, StringTypes):
1512
# NOTE: Could raise a ``UnicodeDecodeError``
1513
return infile.decode(encoding).splitlines(True)
1514
for i, line in enumerate(infile):
1515
if not isinstance(line, unicode):
1516
# NOTE: The isinstance test here handles mixed lists of unicode/string
1517
# NOTE: But the decode will break on any non-string values
1518
# NOTE: Or could raise a ``UnicodeDecodeError``
1519
infile[i] = line.decode(encoding)
1523
def _decode_element(self, line):
1524
"""Decode element to unicode if necessary."""
1525
if not self.encoding:
1527
if isinstance(line, str) and self.default_encoding:
1528
return line.decode(self.default_encoding)
1532
def _str(self, value):
1534
Used by ``stringify`` within validate, to turn non-string values
1537
if not isinstance(value, StringTypes):
1543
def _parse(self, infile):
1544
"""Actually parse the config file."""
1545
temp_list_values = self.list_values
1547
self.list_values = False
1552
maxline = len(infile) - 1
1554
reset_comment = False
1556
while cur_index < maxline:
1560
line = infile[cur_index]
1561
sline = line.strip()
1562
# do we have anything on the line ?
1563
if not sline or sline.startswith('#'):
1564
reset_comment = False
1565
comment_list.append(line)
1569
# preserve initial comment
1570
self.initial_comment = comment_list
1574
reset_comment = True
1575
# first we check if it's a section marker
1576
mat = self._sectionmarker.match(line)
1579
(indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1580
if indent and (self.indent_type is None):
1581
self.indent_type = indent
1582
cur_depth = sect_open.count('[')
1583
if cur_depth != sect_close.count(']'):
1584
self._handle_error("Cannot compute the section depth at line %s.",
1585
NestingError, infile, cur_index)
1588
if cur_depth < this_section.depth:
1589
# the new section is dropping back to a previous level
1591
parent = self._match_depth(this_section,
1594
self._handle_error("Cannot compute nesting level at line %s.",
1595
NestingError, infile, cur_index)
1597
elif cur_depth == this_section.depth:
1598
# the new section is a sibling of the current section
1599
parent = this_section.parent
1600
elif cur_depth == this_section.depth + 1:
1601
# the new section is a child the current section
1602
parent = this_section
1604
self._handle_error("Section too nested at line %s.",
1605
NestingError, infile, cur_index)
1607
sect_name = self._unquote(sect_name)
1608
if parent.has_key(sect_name):
1609
self._handle_error('Duplicate section name at line %s.',
1610
DuplicateError, infile, cur_index)
1613
# create the new section
1614
this_section = Section(
1619
parent[sect_name] = this_section
1620
parent.inline_comments[sect_name] = comment
1621
parent.comments[sect_name] = comment_list
1624
# it's not a section marker,
1625
# so it should be a valid ``key = value`` line
1626
mat = self._keyword.match(line)
1628
# it neither matched as a keyword
1629
# or a section marker
1631
'Invalid line at line "%s".',
1632
ParseError, infile, cur_index)
1634
# is a keyword value
1635
# value will include any inline comment
1636
(indent, key, value) = mat.groups()
1637
if indent and (self.indent_type is None):
1638
self.indent_type = indent
1639
# check for a multiline value
1640
if value[:3] in ['"""', "'''"]:
1642
(value, comment, cur_index) = self._multiline(
1643
value, infile, cur_index, maxline)
1646
'Parse error in value at line %s.',
1647
ParseError, infile, cur_index)
1653
value = unrepr(value)
1654
except Exception, e:
1655
if type(e) == UnknownType:
1656
msg = 'Unknown name or type in value at line %s.'
1658
msg = 'Parse error in value at line %s.'
1659
self._handle_error(msg, UnreprError, infile,
1666
value = unrepr(value)
1667
except Exception, e:
1668
if isinstance(e, UnknownType):
1669
msg = 'Unknown name or type in value at line %s.'
1671
msg = 'Parse error in value at line %s.'
1672
self._handle_error(msg, UnreprError, infile,
1676
# extract comment and lists
1678
(value, comment) = self._handle_value(value)
1681
'Parse error in value at line %s.',
1682
ParseError, infile, cur_index)
1685
key = self._unquote(key)
1686
if this_section.has_key(key):
1688
'Duplicate keyword name at line %s.',
1689
DuplicateError, infile, cur_index)
1692
# we set unrepr because if we have got this far we will never
1693
# be creating a new section
1694
this_section.__setitem__(key, value, unrepr=True)
1695
this_section.inline_comments[key] = comment
1696
this_section.comments[key] = comment_list
1699
if self.indent_type is None:
1700
# no indentation used, set the type accordingly
1701
self.indent_type = ''
1703
# preserve the final comment
1704
if not self and not self.initial_comment:
1705
self.initial_comment = comment_list
1706
elif not reset_comment:
1707
self.final_comment = comment_list
1708
self.list_values = temp_list_values
1711
def _match_depth(self, sect, depth):
1713
Given a section and a depth level, walk back through the sections
1714
parents to see if the depth level matches a previous section.
1716
Return a reference to the right section,
1717
or raise a SyntaxError.
1719
while depth < sect.depth:
1720
if sect is sect.parent:
1721
# we've reached the top level already
1724
if sect.depth == depth:
1726
# shouldn't get here
1730
def _handle_error(self, text, ErrorClass, infile, cur_index):
1732
Handle an error according to the error settings.
1734
Either raise the error or store it.
1735
The error will have occured at ``cur_index``
1737
line = infile[cur_index]
1739
message = text % cur_index
1740
error = ErrorClass(message, cur_index, line)
1741
if self.raise_errors:
1742
# raise the error - parsing stops here
1745
# reraise when parsing has finished
1746
self._errors.append(error)
1749
def _unquote(self, value):
1750
"""Return an unquoted version of a value"""
1751
if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1756
def _quote(self, value, multiline=True):
1758
Return a safely quoted version of a value.
1760
Raise a ConfigObjError if the value cannot be safely quoted.
1761
If multiline is ``True`` (default) then use triple quotes
1764
Don't quote values that don't need it.
1765
Recursively quote members of a list and return a comma joined list.
1766
Multiline is ``False`` for lists.
1767
Obey list syntax for empty and single member lists.
1769
If ``list_values=False`` then the value is only quoted if it contains
1770
a ``\n`` (is multiline) or '#'.
1772
If ``write_empty_values`` is set, and the value is an empty string, it
1775
if multiline and self.write_empty_values and value == '':
1776
# Only if multiline is set, so that it is used for values not
1777
# keys, and not values that are part of a list
1780
if multiline and isinstance(value, (list, tuple)):
1783
elif len(value) == 1:
1784
return self._quote(value[0], multiline=False) + ','
1785
return ', '.join([self._quote(val, multiline=False)
1787
if not isinstance(value, StringTypes):
1791
raise TypeError('Value "%s" is not a string.' % value)
1796
no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1797
need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1798
hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1799
check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1801
if check_for_single:
1802
if not self.list_values:
1803
# we don't quote if ``list_values=False``
1805
# for normal values either single or double quotes will do
1807
# will only happen if multiline is off - e.g. '\n' in key
1808
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1809
elif ((value[0] not in wspace_plus) and
1810
(value[-1] not in wspace_plus) and
1811
(',' not in value)):
1814
quot = self._get_single_quote(value)
1816
# if value has '\n' or "'" *and* '"', it will need triple quotes
1817
quot = self._get_triple_quote(value)
1819
if quot == noquot and '#' in value and self.list_values:
1820
quot = self._get_single_quote(value)
1825
def _get_single_quote(self, value):
1826
if ("'" in value) and ('"' in value):
1827
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1835
def _get_triple_quote(self, value):
1836
if (value.find('"""') != -1) and (value.find("'''") != -1):
1837
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1838
if value.find('"""') == -1:
1845
def _handle_value(self, value):
1847
Given a value string, unquote, remove comment,
1848
handle lists. (including empty and single member lists)
1850
# do we look for lists in values ?
1851
if not self.list_values:
1852
mat = self._nolistvalue.match(value)
1855
# NOTE: we don't unquote here
1858
mat = self._valueexp.match(value)
1860
# the value is badly constructed, probably badly quoted,
1861
# or an invalid list
1863
(list_values, single, empty_list, comment) = mat.groups()
1864
if (list_values == '') and (single is None):
1865
# change this if you want to accept empty values
1867
# NOTE: note there is no error handling from here if the regex
1868
# is wrong: then incorrect values will slip through
1869
if empty_list is not None:
1870
# the single comma - meaning an empty list
1871
return ([], comment)
1872
if single is not None:
1873
# handle empty values
1874
if list_values and not single:
1875
# FIXME: the '' is a workaround because our regex now matches
1876
# '' at the end of a list if it has a trailing comma
1879
single = single or '""'
1880
single = self._unquote(single)
1881
if list_values == '':
1883
return (single, comment)
1884
the_list = self._listvalueexp.findall(list_values)
1885
the_list = [self._unquote(val) for val in the_list]
1886
if single is not None:
1887
the_list += [single]
1888
return (the_list, comment)
1891
def _multiline(self, value, infile, cur_index, maxline):
1892
"""Extract the value, where we are in a multiline situation."""
1894
newvalue = value[3:]
1895
single_line = self._triple_quote[quot][0]
1896
multi_line = self._triple_quote[quot][1]
1897
mat = single_line.match(value)
1899
retval = list(mat.groups())
1900
retval.append(cur_index)
1902
elif newvalue.find(quot) != -1:
1903
# somehow the triple quote is missing
1906
while cur_index < maxline:
1909
line = infile[cur_index]
1910
if line.find(quot) == -1:
1913
# end of multiline, process it
1916
# we've got to the end of the config, oops...
1918
mat = multi_line.match(line)
1920
# a badly formed line
1922
(value, comment) = mat.groups()
1923
return (newvalue + value, comment, cur_index)
1926
def _handle_configspec(self, configspec):
1927
"""Parse the configspec."""
1928
# FIXME: Should we check that the configspec was created with the
1929
# correct settings ? (i.e. ``list_values=False``)
1930
if not isinstance(configspec, ConfigObj):
1932
configspec = ConfigObj(configspec,
1936
except ConfigObjError, e:
1937
# FIXME: Should these errors have a reference
1938
# to the already parsed ConfigObj ?
1939
raise ConfigspecError('Parsing configspec failed: %s' % e)
1941
raise IOError('Reading configspec failed: %s' % e)
1943
self._set_configspec_value(configspec, self)
1946
def _set_configspec_value(self, configspec, section):
1947
"""Used to recursively set configspec values."""
1948
if '__many__' in configspec.sections:
1949
section.configspec['__many__'] = configspec['__many__']
1950
if len(configspec.sections) > 1:
1951
# FIXME: can we supply any useful information here ?
1952
raise RepeatSectionError()
1954
if getattr(configspec, 'initial_comment', None) is not None:
1955
section._configspec_initial_comment = configspec.initial_comment
1956
section._configspec_final_comment = configspec.final_comment
1957
section._configspec_encoding = configspec.encoding
1958
section._configspec_BOM = configspec.BOM
1959
section._configspec_newlines = configspec.newlines
1960
section._configspec_indent_type = configspec.indent_type
1962
for entry in configspec.scalars:
1963
section._configspec_comments[entry] = configspec.comments[entry]
1964
section._configspec_inline_comments[entry] = configspec.inline_comments[entry]
1965
section.configspec[entry] = configspec[entry]
1966
section._order.append(entry)
1968
for entry in configspec.sections:
1969
if entry == '__many__':
1972
section._cs_section_comments[entry] = configspec.comments[entry]
1973
section._cs_section_inline_comments[entry] = configspec.inline_comments[entry]
1974
if not section.has_key(entry):
1976
self._set_configspec_value(configspec[entry], section[entry])
1979
def _handle_repeat(self, section, configspec):
1980
"""Dynamically assign configspec for repeated section."""
1982
section_keys = configspec.sections
1983
scalar_keys = configspec.scalars
1984
except AttributeError:
1985
section_keys = [entry for entry in configspec
1986
if isinstance(configspec[entry], dict)]
1987
scalar_keys = [entry for entry in configspec
1988
if not isinstance(configspec[entry], dict)]
1990
if '__many__' in section_keys and len(section_keys) > 1:
1991
# FIXME: can we supply any useful information here ?
1992
raise RepeatSectionError()
1996
for entry in scalar_keys:
1997
val = configspec[entry]
1998
scalars[entry] = val
1999
for entry in section_keys:
2000
val = configspec[entry]
2001
if entry == '__many__':
2002
scalars[entry] = val
2004
sections[entry] = val
2006
section.configspec = scalars
2007
for entry in sections:
2008
if not section.has_key(entry):
2010
self._handle_repeat(section[entry], sections[entry])
2013
def _write_line(self, indent_string, entry, this_entry, comment):
2014
"""Write an individual line, for the write method"""
2015
# NOTE: the calls to self._quote here handles non-StringType values.
2017
val = self._decode_element(self._quote(this_entry))
2019
val = repr(this_entry)
2020
return '%s%s%s%s%s' % (indent_string,
2021
self._decode_element(self._quote(entry, multiline=False)),
2022
self._a_to_u(' = '),
2024
self._decode_element(comment))
2027
def _write_marker(self, indent_string, depth, entry, comment):
2028
"""Write a section marker line"""
2029
return '%s%s%s%s%s' % (indent_string,
2030
self._a_to_u('[' * depth),
2031
self._quote(self._decode_element(entry), multiline=False),
2032
self._a_to_u(']' * depth),
2033
self._decode_element(comment))
2036
def _handle_comment(self, comment):
2037
"""Deal with a comment."""
2040
start = self.indent_type
2041
if not comment.startswith('#'):
2042
start += self._a_to_u(' # ')
2043
return (start + comment)
2048
def write(self, outfile=None, section=None):
2050
Write the current ConfigObj as a file
2052
tekNico: FIXME: use StringIO instead of real files
2054
>>> filename = a.filename
2055
>>> a.filename = 'test.ini'
2057
>>> a.filename = filename
2058
>>> a == ConfigObj('test.ini', raise_errors=True)
2061
if self.indent_type is None:
2062
# this can be true if initialised from a dictionary
2063
self.indent_type = DEFAULT_INDENT_TYPE
2066
cs = self._a_to_u('#')
2067
csp = self._a_to_u('# ')
2069
int_val = self.interpolation
2070
self.interpolation = False
2072
for line in self.initial_comment:
2073
line = self._decode_element(line)
2074
stripped_line = line.strip()
2075
if stripped_line and not stripped_line.startswith(cs):
2079
indent_string = self.indent_type * section.depth
2080
for entry in (section.scalars + section.sections):
2081
if entry in section.defaults:
2082
# don't write out default values
2084
for comment_line in section.comments[entry]:
2085
comment_line = self._decode_element(comment_line.lstrip())
2086
if comment_line and not comment_line.startswith(cs):
2087
comment_line = csp + comment_line
2088
out.append(indent_string + comment_line)
2089
this_entry = section[entry]
2090
comment = self._handle_comment(section.inline_comments[entry])
2092
if isinstance(this_entry, dict):
2094
out.append(self._write_marker(
2099
out.extend(self.write(section=this_entry))
2101
out.append(self._write_line(
2108
for line in self.final_comment:
2109
line = self._decode_element(line)
2110
stripped_line = line.strip()
2111
if stripped_line and not stripped_line.startswith(cs):
2114
self.interpolation = int_val
2116
if section is not self:
2119
if (self.filename is None) and (outfile is None):
2120
# output a list of lines
2121
# might need to encode
2122
# NOTE: This will *screw* UTF16, each line will start with the BOM
2124
out = [l.encode(self.encoding) for l in out]
2125
if (self.BOM and ((self.encoding is None) or
2126
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2130
out[0] = BOM_UTF8 + out[0]
2133
# Turn the list to a string, joined with correct newlines
2134
newline = self.newlines or os.linesep
2135
output = self._a_to_u(newline).join(out)
2137
output = output.encode(self.encoding)
2138
if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2140
output = BOM_UTF8 + output
2142
if not output.endswith(newline):
2144
if outfile is not None:
2145
outfile.write(output)
2147
h = open(self.filename, 'wb')
2152
def validate(self, validator, preserve_errors=False, copy=False,
2155
Test the ConfigObj against a configspec.
2157
It uses the ``validator`` object from *validate.py*.
2159
To run ``validate`` on the current ConfigObj, call: ::
2161
test = config.validate(validator)
2163
(Normally having previously passed in the configspec when the ConfigObj
2164
was created - you can dynamically assign a dictionary of checks to the
2165
``configspec`` attribute of a section though).
2167
It returns ``True`` if everything passes, or a dictionary of
2168
pass/fails (True/False). If every member of a subsection passes, it
2169
will just have the value ``True``. (It also returns ``False`` if all
2172
In addition, it converts the values from strings to their native
2173
types if their checks pass (and ``stringify`` is set).
2175
If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2176
of a marking a fail with a ``False``, it will preserve the actual
2177
exception object. This can contain info about the reason for failure.
2178
For example the ``VdtValueTooSmallError`` indicates that the value
2179
supplied was too small. If a value (or section) is missing it will
2180
still be marked as ``False``.
2182
You must have the validate module to use ``preserve_errors=True``.
2184
You can then use the ``flatten_errors`` function to turn your nested
2185
results dictionary into a flattened list of failures - useful for
2186
displaying meaningful error messages.
2189
if self.configspec is None:
2190
raise ValueError('No configspec supplied.')
2192
# We do this once to remove a top level dependency on the validate module
2193
# Which makes importing configobj faster
2194
from validate import VdtMissingValue
2195
self._vdtMissingValue = VdtMissingValue
2198
spec_section = section.configspec
2199
if copy and getattr(section, '_configspec_initial_comment', None) is not None:
2200
section.initial_comment = section._configspec_initial_comment
2201
section.final_comment = section._configspec_final_comment
2202
section.encoding = section._configspec_encoding
2203
section.BOM = section._configspec_BOM
2204
section.newlines = section._configspec_newlines
2205
section.indent_type = section._configspec_indent_type
2207
if '__many__' in section.configspec:
2208
many = spec_section['__many__']
2209
# dynamically assign the configspecs
2210
# for the sections below
2211
for entry in section.sections:
2212
self._handle_repeat(section[entry], many)
2217
order = [k for k in section._order if k in spec_section]
2218
order += [k for k in spec_section if k not in order]
2220
if entry == '__many__':
2222
if (not entry in section.scalars) or (entry in section.defaults):
2224
# or entries from defaults
2227
if copy and not entry in section.scalars:
2229
section.comments[entry] = (
2230
section._configspec_comments.get(entry, []))
2231
section.inline_comments[entry] = (
2232
section._configspec_inline_comments.get(entry, ''))
2236
val = section[entry]
2238
check = validator.check(spec_section[entry],
2242
except validator.baseErrorClass, e:
2243
if not preserve_errors or isinstance(e, self._vdtMissingValue):
2246
# preserve the error
2252
section.default_values.pop(entry, None)
2253
except AttributeError:
2254
# For Python 2.2 compatibility
2256
del section.default_values[entry]
2260
if getattr(validator, 'get_default_value', None) is not None:
2262
section.default_values[entry] = validator.get_default_value(spec_section[entry])
2269
if self.stringify or missing:
2270
# if we are doing type conversion
2271
# or the value is a supplied default
2272
if not self.stringify:
2273
if isinstance(check, (list, tuple)):
2275
check = [self._str(item) for item in check]
2276
elif missing and check is None:
2277
# convert the None from a default to a ''
2280
check = self._str(check)
2281
if (check != val) or missing:
2282
section[entry] = check
2283
if not copy and missing and entry not in section.defaults:
2284
section.defaults.append(entry)
2285
# Missing sections will have been created as empty ones when the
2286
# configspec was read.
2287
for entry in section.sections:
2288
# FIXME: this means DEFAULT is not copied in copy mode
2289
if section is self and entry == 'DEFAULT':
2292
section.comments[entry] = section._cs_section_comments[entry]
2293
section.inline_comments[entry] = (
2294
section._cs_section_inline_comments[entry])
2295
check = self.validate(validator, preserve_errors=preserve_errors,
2296
copy=copy, section=section[entry])
2314
"""Clear ConfigObj instance and restore to 'freshly created' state."""
2317
# FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2318
# requires an empty dictionary
2319
self.configspec = None
2320
# Just to be sure ;-)
2321
self._original_configspec = None
2326
Reload a ConfigObj from file.
2328
This method raises a ``ReloadError`` if the ConfigObj doesn't have
2329
a filename attribute pointing to a file.
2331
if not isinstance(self.filename, StringTypes):
2334
filename = self.filename
2335
current_options = {}
2336
for entry in OPTION_DEFAULTS:
2337
if entry == 'configspec':
2339
current_options[entry] = getattr(self, entry)
2341
configspec = self._original_configspec
2342
current_options['configspec'] = configspec
2345
self._initialise(current_options)
2346
self._load(filename, configspec)
2350
class SimpleVal(object):
2353
Can be used to check that all members expected are present.
2355
To use it, provide a configspec with all your members in (the value given
2356
will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2357
method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2358
members are present, or a dictionary with True/False meaning
2359
present/missing. (Whole missing sections will be replaced with ``False``)
2363
self.baseErrorClass = ConfigObjError
2365
def check(self, check, member, missing=False):
2366
"""A dummy check method, always returns the value unchanged."""
2368
raise self.baseErrorClass()
2372
# Check / processing functions for options
2373
def flatten_errors(cfg, res, levels=None, results=None):
2375
An example function that will turn a nested dictionary of results
2376
(as returned by ``ConfigObj.validate``) into a flat list.
2378
``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2379
dictionary returned by ``validate``.
2381
(This is a recursive function, so you shouldn't use the ``levels`` or
2382
``results`` arguments - they are used by the function.
2384
Returns a list of keys that failed. Each member of the list is a tuple :
2387
([list of sections...], key, result)
2389
If ``validate`` was called with ``preserve_errors=False`` (the default)
2390
then ``result`` will always be ``False``.
2392
*list of sections* is a flattened list of sections that the key was found
2395
If the section was missing then key will be ``None``.
2397
If the value (or section) was missing then ``result`` will be ``False``.
2399
If ``validate`` was called with ``preserve_errors=True`` and a value
2400
was present, but failed the check, then ``result`` will be the exception
2401
object returned. You can use this as a string that describes the failure.
2403
For example *The value "3" is of the wrong type*.
2406
>>> vtor = validate.Validator()
2412
... another_option = Probably
2414
... another_option = True
2421
... option1 = boolean()
2422
... option2 = boolean()
2423
... option3 = boolean(default=Bad_value)
2425
... option1 = boolean()
2426
... option2 = boolean()
2427
... option3 = boolean(default=Bad_value)
2429
... another_option = boolean()
2431
... another_option = boolean()
2434
... value2 = integer
2435
... value3 = integer(0, 10)
2436
... [[[section3b-sub]]]
2439
... another_option = boolean()
2441
>>> cs = my_cfg.split('\\n')
2442
>>> ini = my_ini.split('\\n')
2443
>>> cfg = ConfigObj(ini, configspec=cs)
2444
>>> res = cfg.validate(vtor, preserve_errors=True)
2446
>>> for entry in flatten_errors(cfg, res):
2447
... section_list, key, error = entry
2448
... section_list.insert(0, '[root]')
2449
... if key is not None:
2450
... section_list.append(key)
2452
... section_list.append('[missing]')
2453
... section_string = ', '.join(section_list)
2454
... errors.append((section_string, ' = ', error))
2456
>>> for entry in errors:
2457
... print entry[0], entry[1], (entry[2] or 0)
2459
[root], option3 = the value "Bad_value" is of the wrong type.
2460
[root], section1, option2 = 0
2461
[root], section1, option3 = the value "Bad_value" is of the wrong type.
2462
[root], section2, another_option = the value "Probably" is of the wrong type.
2463
[root], section3, section3b, section3b-sub, [missing] = 0
2464
[root], section3, section3b, value2 = the value "a" is of the wrong type.
2465
[root], section3, section3b, value3 = the value "11" is too big.
2466
[root], section4, [missing] = 0
2475
results.append((levels[:], None, False))
2479
for (key, val) in res.items():
2482
if isinstance(cfg.get(key), dict):
2485
flatten_errors(cfg[key], val, levels, results)
2487
results.append((levels[:], key, val))
2496
"""*A programming language is a medium of expression.* - Paul Graham"""