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")
33
from types import StringTypes
34
from warnings import warn
36
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
38
# Python 2.2 does not have these
40
BOM_UTF8 = '\xef\xbb\xbf'
41
# UTF-16, little endian
42
BOM_UTF16_LE = '\xff\xfe'
44
BOM_UTF16_BE = '\xfe\xff'
45
if sys.byteorder == 'little':
46
# UTF-16, native endianness
47
BOM_UTF16 = BOM_UTF16_LE
49
# UTF-16, native endianness
50
BOM_UTF16 = BOM_UTF16_BE
52
# A dictionary mapping BOM to
53
# the encoding to decode with, and what to set the
54
# encoding attribute to.
56
BOM_UTF8: ('utf_8', None),
57
BOM_UTF16_BE: ('utf16_be', 'utf_16'),
58
BOM_UTF16_LE: ('utf16_le', 'utf_16'),
59
BOM_UTF16: ('utf_16', 'utf_16'),
61
# All legal variants of the BOM codecs.
62
# TODO: the list of aliases is not meant to be exhaustive, is there a
69
'utf16_be': 'utf16_be',
70
'utf_16_be': 'utf16_be',
71
'utf-16be': 'utf16_be',
72
'utf16_le': 'utf16_le',
73
'utf_16_le': 'utf16_le',
74
'utf-16le': 'utf16_le',
82
# Map of encodings to the BOM to write.
86
'utf16_be': BOM_UTF16_BE,
87
'utf16_le': BOM_UTF16_LE,
92
def match_utf8(encoding):
93
return BOM_LIST.get(encoding.lower()) == 'utf_8'
96
# Quote strings used for writing values
100
wspace_plus = ' \r\t\n\v\t\'"'
108
"""enumerate for Python 2.2."""
120
__version__ = '4.5.2'
122
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
124
__docformat__ = "restructuredtext en"
128
'DEFAULT_INDENT_TYPE',
129
'DEFAULT_INTERPOLATION',
137
'InterpolationError',
138
'InterpolationLoopError',
139
'MissingInterpolationOption',
140
'RepeatSectionError',
148
DEFAULT_INTERPOLATION = 'configparser'
149
DEFAULT_INDENT_TYPE = ' '
150
MAX_INTERPOL_DEPTH = 10
153
'interpolation': True,
154
'raise_errors': False,
156
'create_empty': False,
160
# option may be set to one of ('', ' ', '\t')
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))
248
class ConfigObjError(SyntaxError):
250
This is the base class for all errors that ConfigObj raises.
251
It is a subclass of SyntaxError.
253
def __init__(self, message='', line_number=None, line=''):
255
self.line_number = line_number
256
self.message = message
257
SyntaxError.__init__(self, message)
260
class NestingError(ConfigObjError):
262
This error indicates a level of nesting that doesn't match.
266
class ParseError(ConfigObjError):
268
This error indicates that a line is badly written.
269
It is neither a valid ``key = value`` line,
270
nor a valid section marker line.
274
class ReloadError(IOError):
276
A 'reload' operation failed.
277
This exception is a subclass of ``IOError``.
280
IOError.__init__(self, 'reload failed, filename is not set.')
283
class DuplicateError(ConfigObjError):
285
The keyword or section specified already exists.
289
class ConfigspecError(ConfigObjError):
291
An error occured whilst parsing a configspec.
295
class InterpolationError(ConfigObjError):
296
"""Base class for the two interpolation errors."""
299
class InterpolationLoopError(InterpolationError):
300
"""Maximum interpolation depth exceeded in string interpolation."""
302
def __init__(self, option):
303
InterpolationError.__init__(
305
'interpolation loop detected in value "%s".' % option)
308
class RepeatSectionError(ConfigObjError):
310
This error indicates additional sections in a section with a
311
``__many__`` (repeated) section.
315
class MissingInterpolationOption(InterpolationError):
316
"""A value specified for interpolation was missing."""
318
def __init__(self, option):
319
InterpolationError.__init__(
321
'missing option "%s" in interpolation.' % option)
324
class UnreprError(ConfigObjError):
325
"""An error parsing in unrepr mode."""
329
class InterpolationEngine(object):
331
A helper class to help perform string interpolation.
333
This class is an abstract base class; its descendants perform
337
# compiled regexp to use in self.interpolate()
338
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
340
def __init__(self, section):
341
# the Section instance that "owns" this engine
342
self.section = section
345
def interpolate(self, key, value):
346
def recursive_interpolate(key, value, section, backtrail):
347
"""The function that does the actual work.
349
``value``: the string we're trying to interpolate.
350
``section``: the section in which that string was found
351
``backtrail``: a dict to keep track of where we've been,
352
to detect and prevent infinite recursion loops
354
This is similar to a depth-first-search algorithm.
356
# Have we been here already?
357
if backtrail.has_key((key, section.name)):
358
# Yes - infinite loop detected
359
raise InterpolationLoopError(key)
360
# Place a marker on our backtrail so we won't come back here again
361
backtrail[(key, section.name)] = 1
363
# Now start the actual work
364
match = self._KEYCRE.search(value)
366
# The actual parsing of the match is implementation-dependent,
367
# so delegate to our helper function
368
k, v, s = self._parse_match(match)
370
# That's the signal that no further interpolation is needed
373
# Further interpolation may be needed to obtain final value
374
replacement = recursive_interpolate(k, v, s, backtrail)
375
# Replace the matched string with its final value
376
start, end = match.span()
377
value = ''.join((value[:start], replacement, value[end:]))
378
new_search_start = start + len(replacement)
379
# Pick up the next interpolation key, if any, for next time
380
# through the while loop
381
match = self._KEYCRE.search(value, new_search_start)
383
# Now safe to come back here again; remove marker from backtrail
384
del backtrail[(key, section.name)]
388
# Back in interpolate(), all we have to do is kick off the recursive
389
# function with appropriate starting values
390
value = recursive_interpolate(key, value, self.section, {})
394
def _fetch(self, key):
395
"""Helper function to fetch values from owning section.
397
Returns a 2-tuple: the value, and the section where it was found.
399
# switch off interpolation before we try and fetch anything !
400
save_interp = self.section.main.interpolation
401
self.section.main.interpolation = False
403
# Start at section that "owns" this InterpolationEngine
404
current_section = self.section
406
# try the current section first
407
val = current_section.get(key)
411
val = current_section.get('DEFAULT', {}).get(key)
414
# move up to parent and try again
415
# top-level's parent is itself
416
if current_section.parent is current_section:
417
# reached top level, time to give up
419
current_section = current_section.parent
421
# restore interpolation to previous value before returning
422
self.section.main.interpolation = save_interp
424
raise MissingInterpolationOption(key)
425
return val, current_section
428
def _parse_match(self, match):
429
"""Implementation-dependent helper function.
431
Will be passed a match object corresponding to the interpolation
432
key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
433
key in the appropriate config file section (using the ``_fetch()``
434
helper function) and return a 3-tuple: (key, value, section)
436
``key`` is the name of the key we're looking for
437
``value`` is the value found for that key
438
``section`` is a reference to the section where it was found
440
``key`` and ``section`` should be None if no further
441
interpolation should be performed on the resulting value
442
(e.g., if we interpolated "$$" and returned "$").
444
raise NotImplementedError()
448
class ConfigParserInterpolation(InterpolationEngine):
449
"""Behaves like ConfigParser."""
450
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
452
def _parse_match(self, match):
454
value, section = self._fetch(key)
455
return key, value, section
459
class TemplateInterpolation(InterpolationEngine):
460
"""Behaves like string.Template."""
462
_KEYCRE = re.compile(r"""
464
(?P<escaped>\$) | # Two $ signs
465
(?P<named>[_a-z][_a-z0-9]*) | # $name format
466
{(?P<braced>[^}]*)} # ${name} format
468
""", re.IGNORECASE | re.VERBOSE)
470
def _parse_match(self, match):
471
# Valid name (in or out of braces): fetch value from section
472
key = match.group('named') or match.group('braced')
474
value, section = self._fetch(key)
475
return key, value, section
476
# Escaped delimiter (e.g., $$): return single delimiter
477
if match.group('escaped') is not None:
478
# Return None for key and section to indicate it's time to stop
479
return None, self._delimiter, None
480
# Anything else: ignore completely, just return it unchanged
481
return None, match.group(), None
484
interpolation_engines = {
485
'configparser': ConfigParserInterpolation,
486
'template': TemplateInterpolation,
493
A dictionary-like object that represents a section in a config file.
495
It does string interpolation if the 'interpolation' attribute
496
of the 'main' object is set to True.
498
Interpolation is tried first from this object, then from the 'DEFAULT'
499
section of this object, next from the parent and its 'DEFAULT' section,
500
and so on until the main object is reached.
502
A Section will behave like an ordered dictionary - following the
503
order of the ``scalars`` and ``sections`` attributes.
504
You can use this to change the order of members.
506
Iteration follows the order: scalars, then sections.
509
def __init__(self, parent, depth, main, indict=None, name=None):
511
* parent is the section above
512
* depth is the depth level of this section
513
* main is the main ConfigObj
514
* indict is a dictionary to initialise the section with
519
# used for nesting level *and* interpolation
521
# used for the interpolation attribute
523
# level of nesting depth of this Section
525
# purely for information
529
# we do this explicitly so that __setitem__ is used properly
530
# (rather than just passing to ``dict.__init__``)
531
for entry, value in indict.iteritems():
535
def _initialise(self):
536
# the sequence of scalar values in this Section
538
# the sequence of sections in this Section
542
self.inline_comments = {}
546
self._configspec_comments = {}
547
self._configspec_inline_comments = {}
548
self._cs_section_comments = {}
549
self._cs_section_inline_comments = {}
552
self.default_values = {}
555
def _interpolate(self, key, value):
557
# do we already have an interpolation engine?
558
engine = self._interpolation_engine
559
except AttributeError:
560
# not yet: first time running _interpolate(), so pick the engine
561
name = self.main.interpolation
562
if name == True: # note that "if name:" would be incorrect here
563
# backwards-compatibility: interpolation=True means use default
564
name = DEFAULT_INTERPOLATION
565
name = name.lower() # so that "Template", "template", etc. all work
566
class_ = interpolation_engines.get(name, None)
568
# invalid value for self.main.interpolation
569
self.main.interpolation = False
572
# save reference to engine so we don't have to do this again
573
engine = self._interpolation_engine = class_(self)
574
# let the engine do the actual work
575
return engine.interpolate(key, value)
578
def __getitem__(self, key):
579
"""Fetch the item and do string interpolation."""
580
val = dict.__getitem__(self, key)
581
if self.main.interpolation and isinstance(val, StringTypes):
582
return self._interpolate(key, val)
586
def __setitem__(self, key, value, unrepr=False):
588
Correctly set a value.
590
Making dictionary values Section instances.
591
(We have to special case 'Section' instances - which are also dicts)
593
Keys must be strings.
594
Values need only be strings (or lists of strings) if
595
``main.stringify`` is set.
597
`unrepr`` must be set when setting a value to a dictionary, without
598
creating a new sub-section.
600
if not isinstance(key, StringTypes):
601
raise ValueError('The key "%s" is not a string.' % key)
604
if not self.comments.has_key(key):
605
self.comments[key] = []
606
self.inline_comments[key] = ''
607
# remove the entry from defaults
608
if key in self.defaults:
609
self.defaults.remove(key)
611
if isinstance(value, Section):
612
if not self.has_key(key):
613
self.sections.append(key)
614
dict.__setitem__(self, key, value)
615
elif isinstance(value, dict) and not unrepr:
616
# First create the new depth level,
617
# then create the section
618
if not self.has_key(key):
619
self.sections.append(key)
620
new_depth = self.depth + 1
631
if not self.has_key(key):
632
self.scalars.append(key)
633
if not self.main.stringify:
634
if isinstance(value, StringTypes):
636
elif isinstance(value, (list, tuple)):
638
if not isinstance(entry, StringTypes):
639
raise TypeError('Value is not a string "%s".' % entry)
641
raise TypeError('Value is not a string "%s".' % value)
642
dict.__setitem__(self, key, value)
645
def __delitem__(self, key):
646
"""Remove items from the sequence when deleting."""
647
dict. __delitem__(self, key)
648
if key in self.scalars:
649
self.scalars.remove(key)
651
self.sections.remove(key)
652
del self.comments[key]
653
del self.inline_comments[key]
656
def get(self, key, default=None):
657
"""A version of ``get`` that doesn't bypass string interpolation."""
664
def update(self, indict):
666
A version of update that uses our ``__setitem__``.
669
self[entry] = indict[entry]
672
def pop(self, key, *args):
674
'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
675
If key is not found, d is returned if given, otherwise KeyError is raised'
677
val = dict.pop(self, key, *args)
678
if key in self.scalars:
679
del self.comments[key]
680
del self.inline_comments[key]
681
self.scalars.remove(key)
682
elif key in self.sections:
683
del self.comments[key]
684
del self.inline_comments[key]
685
self.sections.remove(key)
686
if self.main.interpolation and isinstance(val, StringTypes):
687
return self._interpolate(key, val)
692
"""Pops the first (key,val)"""
693
sequence = (self.scalars + self.sections)
695
raise KeyError(": 'popitem(): dictionary is empty'")
704
A version of clear that also affects scalars/sections
705
Also clears comments and configspec.
707
Leaves other attributes alone :
708
depth/main/parent are not affected
714
self.inline_comments = {}
718
def setdefault(self, key, default=None):
719
"""A version of setdefault that sets sequence if appropriate."""
728
"""D.items() -> list of D's (key, value) pairs, as 2-tuples"""
729
return zip((self.scalars + self.sections), self.values())
733
"""D.keys() -> list of D's keys"""
734
return (self.scalars + self.sections)
738
"""D.values() -> list of D's values"""
739
return [self[key] for key in (self.scalars + self.sections)]
743
"""D.iteritems() -> an iterator over the (key, value) items of D"""
744
return iter(self.items())
748
"""D.iterkeys() -> an iterator over the keys of D"""
749
return iter((self.scalars + self.sections))
754
def itervalues(self):
755
"""D.itervalues() -> an iterator over the values of D"""
756
return iter(self.values())
760
"""x.__repr__() <==> repr(x)"""
761
return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
762
for key in (self.scalars + self.sections)])
765
__str__.__doc__ = "x.__str__() <==> str(x)"
768
# Extra methods - not in a normal dictionary
772
Return a deepcopy of self as a dictionary.
774
All members that are ``Section`` instances are recursively turned to
775
ordinary dictionaries - by calling their ``dict`` method.
785
this_entry = self[entry]
786
if isinstance(this_entry, Section):
787
this_entry = this_entry.dict()
788
elif isinstance(this_entry, list):
789
# create a copy rather than a reference
790
this_entry = list(this_entry)
791
elif isinstance(this_entry, tuple):
792
# create a copy rather than a reference
793
this_entry = tuple(this_entry)
794
newdict[entry] = this_entry
798
def merge(self, indict):
800
A recursive update - useful for merging config files.
802
>>> a = '''[section1]
805
... more_options = False
806
... # end of file'''.splitlines()
807
>>> b = '''# File is user.ini
810
... # end of file'''.splitlines()
811
>>> c1 = ConfigObj(b)
812
>>> c2 = ConfigObj(a)
815
{'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
817
for key, val in indict.items():
818
if (key in self and isinstance(self[key], dict) and
819
isinstance(val, dict)):
825
def rename(self, oldkey, newkey):
827
Change a keyname to another, without changing position in sequence.
829
Implemented so that transformations can be made on keys,
830
as well as on values. (used by encode and decode)
832
Also renames comments.
834
if oldkey in self.scalars:
835
the_list = self.scalars
836
elif oldkey in self.sections:
837
the_list = self.sections
839
raise KeyError('Key "%s" not found.' % oldkey)
840
pos = the_list.index(oldkey)
843
dict.__delitem__(self, oldkey)
844
dict.__setitem__(self, newkey, val)
845
the_list.remove(oldkey)
846
the_list.insert(pos, newkey)
847
comm = self.comments[oldkey]
848
inline_comment = self.inline_comments[oldkey]
849
del self.comments[oldkey]
850
del self.inline_comments[oldkey]
851
self.comments[newkey] = comm
852
self.inline_comments[newkey] = inline_comment
855
def walk(self, function, raise_errors=True,
856
call_on_sections=False, **keywargs):
858
Walk every member and call a function on the keyword and value.
860
Return a dictionary of the return values
862
If the function raises an exception, raise the errror
863
unless ``raise_errors=False``, in which case set the return value to
866
Any unrecognised keyword arguments you pass to walk, will be pased on
867
to the function you pass in.
869
Note: if ``call_on_sections`` is ``True`` then - on encountering a
870
subsection, *first* the function is called for the *whole* subsection,
871
and then recurses into it's members. This means your function must be
872
able to handle strings, dictionaries and lists. This allows you
873
to change the key of subsections as well as for ordinary members. The
874
return value when called on the whole subsection has to be discarded.
876
See the encode and decode methods for examples, including functions.
880
You can use ``walk`` to transform the names of members of a section
881
but you mustn't add or delete members.
883
>>> config = '''[XXXXsection]
884
... XXXXkey = XXXXvalue'''.splitlines()
885
>>> cfg = ConfigObj(config)
887
{'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
888
>>> def transform(section, key):
889
... val = section[key]
890
... newkey = key.replace('XXXX', 'CLIENT1')
891
... section.rename(key, newkey)
892
... if isinstance(val, (tuple, list, dict)):
895
... val = val.replace('XXXX', 'CLIENT1')
896
... section[newkey] = val
897
>>> cfg.walk(transform, call_on_sections=True)
898
{'CLIENT1section': {'CLIENT1key': None}}
900
{'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
904
for i in range(len(self.scalars)):
905
entry = self.scalars[i]
907
val = function(self, entry, **keywargs)
908
# bound again in case name has changed
909
entry = self.scalars[i]
915
entry = self.scalars[i]
918
for i in range(len(self.sections)):
919
entry = self.sections[i]
922
function(self, entry, **keywargs)
927
entry = self.sections[i]
929
# bound again in case name has changed
930
entry = self.sections[i]
931
# previous result is discarded
932
out[entry] = self[entry].walk(
934
raise_errors=raise_errors,
935
call_on_sections=call_on_sections,
940
def decode(self, encoding):
942
Decode all strings and values to unicode, using the specified encoding.
944
Works with subsections and list values.
946
Uses the ``walk`` method.
948
Testing ``encode`` and ``decode``.
950
>>> m.decode('ascii')
951
>>> def testuni(val):
952
... for entry in val:
953
... if not isinstance(entry, unicode):
954
... print >> sys.stderr, type(entry)
955
... raise AssertionError, 'decode failed.'
956
... if isinstance(val[entry], dict):
957
... testuni(val[entry])
958
... elif not isinstance(val[entry], unicode):
959
... raise AssertionError, 'decode failed.'
961
>>> m.encode('ascii')
965
warn('use of ``decode`` is deprecated.', DeprecationWarning)
966
def decode(section, key, encoding=encoding, warn=True):
969
if isinstance(val, (list, tuple)):
972
newval.append(entry.decode(encoding))
973
elif isinstance(val, dict):
976
newval = val.decode(encoding)
977
newkey = key.decode(encoding)
978
section.rename(key, newkey)
979
section[newkey] = newval
980
# using ``call_on_sections`` allows us to modify section names
981
self.walk(decode, call_on_sections=True)
984
def encode(self, encoding):
986
Encode all strings and values from unicode,
987
using the specified encoding.
989
Works with subsections and list values.
990
Uses the ``walk`` method.
992
warn('use of ``encode`` is deprecated.', DeprecationWarning)
993
def encode(section, key, encoding=encoding):
996
if isinstance(val, (list, tuple)):
999
newval.append(entry.encode(encoding))
1000
elif isinstance(val, dict):
1003
newval = val.encode(encoding)
1004
newkey = key.encode(encoding)
1005
section.rename(key, newkey)
1006
section[newkey] = newval
1007
self.walk(encode, call_on_sections=True)
1010
def istrue(self, key):
1011
"""A deprecated version of ``as_bool``."""
1012
warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
1013
'instead.', DeprecationWarning)
1014
return self.as_bool(key)
1017
def as_bool(self, key):
1019
Accepts a key as input. The corresponding value must be a string or
1020
the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
1021
retain compatibility with Python 2.2.
1023
If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
1026
If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
1029
``as_bool`` is not case sensitive.
1031
Any other input will raise a ``ValueError``.
1036
Traceback (most recent call last):
1037
ValueError: Value "fish" is neither True nor False
1052
if not isinstance(val, StringTypes):
1053
# TODO: Why do we raise a KeyError here?
1056
return self.main._bools[val.lower()]
1058
raise ValueError('Value "%s" is neither True nor False' % val)
1061
def as_int(self, key):
1063
A convenience method which coerces the specified value to an integer.
1065
If the value is an invalid literal for ``int``, a ``ValueError`` will
1071
Traceback (most recent call last):
1072
ValueError: invalid literal for int(): fish
1078
Traceback (most recent call last):
1079
ValueError: invalid literal for int(): 3.2
1081
return int(self[key])
1084
def as_float(self, key):
1086
A convenience method which coerces the specified value to a float.
1088
If the value is an invalid literal for ``float``, a ``ValueError`` will
1094
Traceback (most recent call last):
1095
ValueError: invalid literal for float(): fish
1103
return float(self[key])
1106
def restore_default(self, key):
1108
Restore (and return) default value for the specified key.
1110
This method will only work for a ConfigObj that was created
1111
with a configspec and has been validated.
1113
If there is no default value for this key, ``KeyError`` is raised.
1115
default = self.default_values[key]
1116
dict.__setitem__(self, key, default)
1117
if key not in self.defaults:
1118
self.defaults.append(key)
1122
def restore_defaults(self):
1124
Recursively restore default values to all members
1127
This method will only work for a ConfigObj that was created
1128
with a configspec and has been validated.
1130
It doesn't delete or modify entries without default values.
1132
for key in self.default_values:
1133
self.restore_default(key)
1135
for section in self.sections:
1136
self[section].restore_defaults()
1139
class ConfigObj(Section):
1140
"""An object to read, create, and write config files."""
1142
_keyword = re.compile(r'''^ # line start
1145
(?:".*?")| # double quotes
1146
(?:'.*?')| # single quotes
1147
(?:[^'"=].*?) # no quotes
1150
(.*) # value (including list values and comments)
1155
_sectionmarker = re.compile(r'''^
1156
(\s*) # 1: indentation
1157
((?:\[\s*)+) # 2: section marker open
1158
( # 3: section name open
1159
(?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1160
(?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1161
(?:[^'"\s].*?) # at least one non-space unquoted
1162
) # section name close
1163
((?:\s*\])+) # 4: section marker close
1164
\s*(\#.*)? # 5: optional comment
1168
# this regexp pulls list values out as a single string
1169
# or single values and comments
1170
# FIXME: this regex adds a '' to the end of comma terminated lists
1171
# workaround in ``_handle_value``
1172
_valueexp = re.compile(r'''^
1178
(?:".*?")| # double quotes
1179
(?:'.*?')| # single quotes
1180
(?:[^'",\#][^,\#]*?) # unquoted
1183
)* # match all list items ending in a comma (if any)
1186
(?:".*?")| # double quotes
1187
(?:'.*?')| # single quotes
1188
(?:[^'",\#\s][^,]*?)| # unquoted
1189
(?:(?<!,)) # Empty value
1190
)? # last item in a list - or string value
1192
(,) # alternatively a single comma - empty list
1194
\s*(\#.*)? # optional comment
1198
# use findall to get the members of a list value
1199
_listvalueexp = re.compile(r'''
1201
(?:".*?")| # double quotes
1202
(?:'.*?')| # single quotes
1203
(?:[^'",\#].*?) # unquoted
1209
# this regexp is used for the value
1210
# when lists are switched off
1211
_nolistvalue = re.compile(r'''^
1213
(?:".*?")| # double quotes
1214
(?:'.*?')| # single quotes
1215
(?:[^'"\#].*?)| # unquoted
1218
\s*(\#.*)? # optional comment
1222
# regexes for finding triple quoted values on one line
1223
_single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1224
_single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1225
_multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1226
_multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1229
"'''": (_single_line_single, _multi_line_single),
1230
'"""': (_single_line_double, _multi_line_double),
1233
# Used by the ``istrue`` Section method
1235
'yes': True, 'no': False,
1236
'on': True, 'off': False,
1237
'1': True, '0': False,
1238
'true': True, 'false': False,
1242
def __init__(self, infile=None, options=None, **kwargs):
1244
Parse a config file or create a config file object.
1246
``ConfigObj(infile=None, options=None, **kwargs)``
1248
# init the superclass
1249
Section.__init__(self, self, 0, self)
1256
options = dict(options)
1258
# keyword arguments take precedence over an options dictionary
1259
options.update(kwargs)
1261
defaults = OPTION_DEFAULTS.copy()
1262
# TODO: check the values too.
1263
for entry in options:
1264
if entry not in defaults:
1265
raise TypeError('Unrecognised option "%s".' % entry)
1267
# Add any explicit options to the defaults
1268
defaults.update(options)
1269
self._initialise(defaults)
1270
configspec = defaults['configspec']
1271
self._original_configspec = configspec
1272
self._load(infile, configspec)
1275
def _load(self, infile, configspec):
1276
if isinstance(infile, StringTypes):
1277
self.filename = infile
1278
if os.path.isfile(infile):
1279
h = open(infile, 'rb')
1280
infile = h.read() or []
1282
elif self.file_error:
1283
# raise an error if the file doesn't exist
1284
raise IOError('Config file not found: "%s".' % self.filename)
1286
# file doesn't already exist
1287
if self.create_empty:
1288
# this is a good test that the filename specified
1289
# isn't impossible - like on a non-existent device
1290
h = open(infile, 'w')
1295
elif isinstance(infile, (list, tuple)):
1296
infile = list(infile)
1298
elif isinstance(infile, dict):
1300
# the Section class handles creating subsections
1301
if isinstance(infile, ConfigObj):
1302
# get a copy of our ConfigObj
1303
infile = infile.dict()
1305
for entry in infile:
1306
self[entry] = infile[entry]
1309
if configspec is not None:
1310
self._handle_configspec(configspec)
1312
self.configspec = None
1315
elif getattr(infile, 'read', None) is not None:
1316
# This supports file like objects
1317
infile = infile.read() or []
1318
# needs splitting into lines - but needs doing *after* decoding
1319
# in case it's not an 8 bit encoding
1321
raise TypeError('infile must be a filename, file like object, or list of lines.')
1324
# don't do it for the empty ConfigObj
1325
infile = self._handle_bom(infile)
1326
# infile is now *always* a list
1328
# Set the newlines attribute (first line ending it finds)
1329
# and strip trailing '\n' or '\r' from lines
1331
if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1333
for end in ('\r\n', '\n', '\r'):
1334
if line.endswith(end):
1339
infile = [line.rstrip('\r\n') for line in infile]
1342
# if we had any errors, now is the time to raise them
1344
info = "at line %s." % self._errors[0].line_number
1345
if len(self._errors) > 1:
1346
msg = "Parsing failed with several errors.\nFirst error %s" % info
1347
error = ConfigObjError(msg)
1349
error = self._errors[0]
1350
# set the errors attribute; it's a list of tuples:
1351
# (error_type, message, line_number)
1352
error.errors = self._errors
1353
# set the config attribute
1356
# delete private attributes
1359
if configspec is None:
1360
self.configspec = None
1362
self._handle_configspec(configspec)
1365
def _initialise(self, options=None):
1367
options = OPTION_DEFAULTS
1369
# initialise a few variables
1370
self.filename = None
1372
self.raise_errors = options['raise_errors']
1373
self.interpolation = options['interpolation']
1374
self.list_values = options['list_values']
1375
self.create_empty = options['create_empty']
1376
self.file_error = options['file_error']
1377
self.stringify = options['stringify']
1378
self.indent_type = options['indent_type']
1379
self.encoding = options['encoding']
1380
self.default_encoding = options['default_encoding']
1382
self.newlines = None
1383
self.write_empty_values = options['write_empty_values']
1384
self.unrepr = options['unrepr']
1386
self.initial_comment = []
1387
self.final_comment = []
1388
self.configspec = {}
1390
# Clear section attributes as well
1391
Section._initialise(self)
1395
return ('ConfigObj({%s})' %
1396
', '.join([('%s: %s' % (repr(key), repr(self[key])))
1397
for key in (self.scalars + self.sections)]))
1400
def _handle_bom(self, infile):
1402
Handle any BOM, and decode if necessary.
1404
If an encoding is specified, that *must* be used - but the BOM should
1405
still be removed (and the BOM attribute set).
1407
(If the encoding is wrongly specified, then a BOM for an alternative
1408
encoding won't be discovered or removed.)
1410
If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1411
removed. The BOM attribute will be set. UTF16 will be decoded to
1414
NOTE: This method must not be called with an empty ``infile``.
1416
Specifying the *wrong* encoding is likely to cause a
1417
``UnicodeDecodeError``.
1419
``infile`` must always be returned as a list of lines, but may be
1420
passed in as a single string.
1422
if ((self.encoding is not None) and
1423
(self.encoding.lower() not in BOM_LIST)):
1424
# No need to check for a BOM
1425
# the encoding specified doesn't have one
1427
return self._decode(infile, self.encoding)
1429
if isinstance(infile, (list, tuple)):
1433
if self.encoding is not None:
1434
# encoding explicitly supplied
1435
# And it could have an associated BOM
1436
# TODO: if encoding is just UTF16 - we ought to check for both
1437
# TODO: big endian and little endian versions.
1438
enc = BOM_LIST[self.encoding.lower()]
1440
# For UTF16 we try big endian and little endian
1441
for BOM, (encoding, final_encoding) in BOMS.items():
1442
if not final_encoding:
1445
if infile.startswith(BOM):
1448
# Don't need to remove BOM
1449
return self._decode(infile, encoding)
1451
# If we get this far, will *probably* raise a DecodeError
1452
# As it doesn't appear to start with a BOM
1453
return self._decode(infile, self.encoding)
1457
if not line.startswith(BOM):
1458
return self._decode(infile, self.encoding)
1460
newline = line[len(BOM):]
1463
if isinstance(infile, (list, tuple)):
1468
return self._decode(infile, self.encoding)
1470
# No encoding specified - so we need to check for UTF8/UTF16
1471
for BOM, (encoding, final_encoding) in BOMS.items():
1472
if not line.startswith(BOM):
1476
self.encoding = final_encoding
1477
if not final_encoding:
1481
newline = line[len(BOM):]
1482
if isinstance(infile, (list, tuple)):
1486
# UTF8 - don't decode
1487
if isinstance(infile, StringTypes):
1488
return infile.splitlines(True)
1491
# UTF16 - have to decode
1492
return self._decode(infile, encoding)
1494
# No BOM discovered and no encoding specified, just return
1495
if isinstance(infile, StringTypes):
1496
# infile read from a file will be a single string
1497
return infile.splitlines(True)
1501
def _a_to_u(self, aString):
1502
"""Decode ASCII strings to unicode if a self.encoding is specified."""
1504
return aString.decode('ascii')
1509
def _decode(self, infile, encoding):
1511
Decode infile to unicode. Using the specified encoding.
1513
if is a string, it also needs converting to a list.
1515
if isinstance(infile, StringTypes):
1517
# NOTE: Could raise a ``UnicodeDecodeError``
1518
return infile.decode(encoding).splitlines(True)
1519
for i, line in enumerate(infile):
1520
if not isinstance(line, unicode):
1521
# NOTE: The isinstance test here handles mixed lists of unicode/string
1522
# NOTE: But the decode will break on any non-string values
1523
# NOTE: Or could raise a ``UnicodeDecodeError``
1524
infile[i] = line.decode(encoding)
1528
def _decode_element(self, line):
1529
"""Decode element to unicode if necessary."""
1530
if not self.encoding:
1532
if isinstance(line, str) and self.default_encoding:
1533
return line.decode(self.default_encoding)
1537
def _str(self, value):
1539
Used by ``stringify`` within validate, to turn non-string values
1542
if not isinstance(value, StringTypes):
1548
def _parse(self, infile):
1549
"""Actually parse the config file."""
1550
temp_list_values = self.list_values
1552
self.list_values = False
1557
maxline = len(infile) - 1
1559
reset_comment = False
1561
while cur_index < maxline:
1565
line = infile[cur_index]
1566
sline = line.strip()
1567
# do we have anything on the line ?
1568
if not sline or sline.startswith('#'):
1569
reset_comment = False
1570
comment_list.append(line)
1574
# preserve initial comment
1575
self.initial_comment = comment_list
1579
reset_comment = True
1580
# first we check if it's a section marker
1581
mat = self._sectionmarker.match(line)
1584
(indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1585
if indent and (self.indent_type is None):
1586
self.indent_type = indent
1587
cur_depth = sect_open.count('[')
1588
if cur_depth != sect_close.count(']'):
1589
self._handle_error("Cannot compute the section depth at line %s.",
1590
NestingError, infile, cur_index)
1593
if cur_depth < this_section.depth:
1594
# the new section is dropping back to a previous level
1596
parent = self._match_depth(this_section,
1599
self._handle_error("Cannot compute nesting level at line %s.",
1600
NestingError, infile, cur_index)
1602
elif cur_depth == this_section.depth:
1603
# the new section is a sibling of the current section
1604
parent = this_section.parent
1605
elif cur_depth == this_section.depth + 1:
1606
# the new section is a child the current section
1607
parent = this_section
1609
self._handle_error("Section too nested at line %s.",
1610
NestingError, infile, cur_index)
1612
sect_name = self._unquote(sect_name)
1613
if parent.has_key(sect_name):
1614
self._handle_error('Duplicate section name at line %s.',
1615
DuplicateError, infile, cur_index)
1618
# create the new section
1619
this_section = Section(
1624
parent[sect_name] = this_section
1625
parent.inline_comments[sect_name] = comment
1626
parent.comments[sect_name] = comment_list
1629
# it's not a section marker,
1630
# so it should be a valid ``key = value`` line
1631
mat = self._keyword.match(line)
1633
# it neither matched as a keyword
1634
# or a section marker
1636
'Invalid line at line "%s".',
1637
ParseError, infile, cur_index)
1639
# is a keyword value
1640
# value will include any inline comment
1641
(indent, key, value) = mat.groups()
1642
if indent and (self.indent_type is None):
1643
self.indent_type = indent
1644
# check for a multiline value
1645
if value[:3] in ['"""', "'''"]:
1647
(value, comment, cur_index) = self._multiline(
1648
value, infile, cur_index, maxline)
1651
'Parse error in value at line %s.',
1652
ParseError, infile, cur_index)
1658
value = unrepr(value)
1659
except Exception, e:
1660
if type(e) == UnknownType:
1661
msg = 'Unknown name or type in value at line %s.'
1663
msg = 'Parse error in value at line %s.'
1664
self._handle_error(msg, UnreprError, infile,
1671
value = unrepr(value)
1672
except Exception, e:
1673
if isinstance(e, UnknownType):
1674
msg = 'Unknown name or type in value at line %s.'
1676
msg = 'Parse error in value at line %s.'
1677
self._handle_error(msg, UnreprError, infile,
1681
# extract comment and lists
1683
(value, comment) = self._handle_value(value)
1686
'Parse error in value at line %s.',
1687
ParseError, infile, cur_index)
1690
key = self._unquote(key)
1691
if this_section.has_key(key):
1693
'Duplicate keyword name at line %s.',
1694
DuplicateError, infile, cur_index)
1697
# we set unrepr because if we have got this far we will never
1698
# be creating a new section
1699
this_section.__setitem__(key, value, unrepr=True)
1700
this_section.inline_comments[key] = comment
1701
this_section.comments[key] = comment_list
1704
if self.indent_type is None:
1705
# no indentation used, set the type accordingly
1706
self.indent_type = ''
1708
# preserve the final comment
1709
if not self and not self.initial_comment:
1710
self.initial_comment = comment_list
1711
elif not reset_comment:
1712
self.final_comment = comment_list
1713
self.list_values = temp_list_values
1716
def _match_depth(self, sect, depth):
1718
Given a section and a depth level, walk back through the sections
1719
parents to see if the depth level matches a previous section.
1721
Return a reference to the right section,
1722
or raise a SyntaxError.
1724
while depth < sect.depth:
1725
if sect is sect.parent:
1726
# we've reached the top level already
1729
if sect.depth == depth:
1731
# shouldn't get here
1735
def _handle_error(self, text, ErrorClass, infile, cur_index):
1737
Handle an error according to the error settings.
1739
Either raise the error or store it.
1740
The error will have occured at ``cur_index``
1742
line = infile[cur_index]
1744
message = text % cur_index
1745
error = ErrorClass(message, cur_index, line)
1746
if self.raise_errors:
1747
# raise the error - parsing stops here
1750
# reraise when parsing has finished
1751
self._errors.append(error)
1754
def _unquote(self, value):
1755
"""Return an unquoted version of a value"""
1756
if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1761
def _quote(self, value, multiline=True):
1763
Return a safely quoted version of a value.
1765
Raise a ConfigObjError if the value cannot be safely quoted.
1766
If multiline is ``True`` (default) then use triple quotes
1769
Don't quote values that don't need it.
1770
Recursively quote members of a list and return a comma joined list.
1771
Multiline is ``False`` for lists.
1772
Obey list syntax for empty and single member lists.
1774
If ``list_values=False`` then the value is only quoted if it contains
1775
a ``\n`` (is multiline) or '#'.
1777
If ``write_empty_values`` is set, and the value is an empty string, it
1780
if multiline and self.write_empty_values and value == '':
1781
# Only if multiline is set, so that it is used for values not
1782
# keys, and not values that are part of a list
1785
if multiline and isinstance(value, (list, tuple)):
1788
elif len(value) == 1:
1789
return self._quote(value[0], multiline=False) + ','
1790
return ', '.join([self._quote(val, multiline=False)
1792
if not isinstance(value, StringTypes):
1796
raise TypeError('Value "%s" is not a string.' % value)
1801
no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1802
need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1803
hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1804
check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1806
if check_for_single:
1807
if not self.list_values:
1808
# we don't quote if ``list_values=False``
1810
# for normal values either single or double quotes will do
1812
# will only happen if multiline is off - e.g. '\n' in key
1813
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1814
elif ((value[0] not in wspace_plus) and
1815
(value[-1] not in wspace_plus) and
1816
(',' not in value)):
1819
quot = self._get_single_quote(value)
1821
# if value has '\n' or "'" *and* '"', it will need triple quotes
1822
quot = self._get_triple_quote(value)
1824
if quot == noquot and '#' in value and self.list_values:
1825
quot = self._get_single_quote(value)
1830
def _get_single_quote(self, value):
1831
if ("'" in value) and ('"' in value):
1832
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1840
def _get_triple_quote(self, value):
1841
if (value.find('"""') != -1) and (value.find("'''") != -1):
1842
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1843
if value.find('"""') == -1:
1850
def _handle_value(self, value):
1852
Given a value string, unquote, remove comment,
1853
handle lists. (including empty and single member lists)
1855
# do we look for lists in values ?
1856
if not self.list_values:
1857
mat = self._nolistvalue.match(value)
1860
# NOTE: we don't unquote here
1863
mat = self._valueexp.match(value)
1865
# the value is badly constructed, probably badly quoted,
1866
# or an invalid list
1868
(list_values, single, empty_list, comment) = mat.groups()
1869
if (list_values == '') and (single is None):
1870
# change this if you want to accept empty values
1872
# NOTE: note there is no error handling from here if the regex
1873
# is wrong: then incorrect values will slip through
1874
if empty_list is not None:
1875
# the single comma - meaning an empty list
1876
return ([], comment)
1877
if single is not None:
1878
# handle empty values
1879
if list_values and not single:
1880
# FIXME: the '' is a workaround because our regex now matches
1881
# '' at the end of a list if it has a trailing comma
1884
single = single or '""'
1885
single = self._unquote(single)
1886
if list_values == '':
1888
return (single, comment)
1889
the_list = self._listvalueexp.findall(list_values)
1890
the_list = [self._unquote(val) for val in the_list]
1891
if single is not None:
1892
the_list += [single]
1893
return (the_list, comment)
1896
def _multiline(self, value, infile, cur_index, maxline):
1897
"""Extract the value, where we are in a multiline situation."""
1899
newvalue = value[3:]
1900
single_line = self._triple_quote[quot][0]
1901
multi_line = self._triple_quote[quot][1]
1902
mat = single_line.match(value)
1904
retval = list(mat.groups())
1905
retval.append(cur_index)
1907
elif newvalue.find(quot) != -1:
1908
# somehow the triple quote is missing
1911
while cur_index < maxline:
1914
line = infile[cur_index]
1915
if line.find(quot) == -1:
1918
# end of multiline, process it
1921
# we've got to the end of the config, oops...
1923
mat = multi_line.match(line)
1925
# a badly formed line
1927
(value, comment) = mat.groups()
1928
return (newvalue + value, comment, cur_index)
1931
def _handle_configspec(self, configspec):
1932
"""Parse the configspec."""
1933
# FIXME: Should we check that the configspec was created with the
1934
# correct settings ? (i.e. ``list_values=False``)
1935
if not isinstance(configspec, ConfigObj):
1937
configspec = ConfigObj(configspec,
1941
except ConfigObjError, e:
1942
# FIXME: Should these errors have a reference
1943
# to the already parsed ConfigObj ?
1944
raise ConfigspecError('Parsing configspec failed: %s' % e)
1946
raise IOError('Reading configspec failed: %s' % e)
1948
self._set_configspec_value(configspec, self)
1951
def _set_configspec_value(self, configspec, section):
1952
"""Used to recursively set configspec values."""
1953
if '__many__' in configspec.sections:
1954
section.configspec['__many__'] = configspec['__many__']
1955
if len(configspec.sections) > 1:
1956
# FIXME: can we supply any useful information here ?
1957
raise RepeatSectionError()
1959
if getattr(configspec, 'initial_comment', None) is not None:
1960
section._configspec_initial_comment = configspec.initial_comment
1961
section._configspec_final_comment = configspec.final_comment
1962
section._configspec_encoding = configspec.encoding
1963
section._configspec_BOM = configspec.BOM
1964
section._configspec_newlines = configspec.newlines
1965
section._configspec_indent_type = configspec.indent_type
1967
for entry in configspec.scalars:
1968
section._configspec_comments[entry] = configspec.comments[entry]
1969
section._configspec_inline_comments[entry] = configspec.inline_comments[entry]
1970
section.configspec[entry] = configspec[entry]
1971
section._order.append(entry)
1973
for entry in configspec.sections:
1974
if entry == '__many__':
1977
section._cs_section_comments[entry] = configspec.comments[entry]
1978
section._cs_section_inline_comments[entry] = configspec.inline_comments[entry]
1979
if not section.has_key(entry):
1981
self._set_configspec_value(configspec[entry], section[entry])
1984
def _handle_repeat(self, section, configspec):
1985
"""Dynamically assign configspec for repeated section."""
1987
section_keys = configspec.sections
1988
scalar_keys = configspec.scalars
1989
except AttributeError:
1990
section_keys = [entry for entry in configspec
1991
if isinstance(configspec[entry], dict)]
1992
scalar_keys = [entry for entry in configspec
1993
if not isinstance(configspec[entry], dict)]
1995
if '__many__' in section_keys and len(section_keys) > 1:
1996
# FIXME: can we supply any useful information here ?
1997
raise RepeatSectionError()
2001
for entry in scalar_keys:
2002
val = configspec[entry]
2003
scalars[entry] = val
2004
for entry in section_keys:
2005
val = configspec[entry]
2006
if entry == '__many__':
2007
scalars[entry] = val
2009
sections[entry] = val
2011
section.configspec = scalars
2012
for entry in sections:
2013
if not section.has_key(entry):
2015
self._handle_repeat(section[entry], sections[entry])
2018
def _write_line(self, indent_string, entry, this_entry, comment):
2019
"""Write an individual line, for the write method"""
2020
# NOTE: the calls to self._quote here handles non-StringType values.
2022
val = self._decode_element(self._quote(this_entry))
2024
val = repr(this_entry)
2025
return '%s%s%s%s%s' % (indent_string,
2026
self._decode_element(self._quote(entry, multiline=False)),
2027
self._a_to_u(' = '),
2029
self._decode_element(comment))
2032
def _write_marker(self, indent_string, depth, entry, comment):
2033
"""Write a section marker line"""
2034
return '%s%s%s%s%s' % (indent_string,
2035
self._a_to_u('[' * depth),
2036
self._quote(self._decode_element(entry), multiline=False),
2037
self._a_to_u(']' * depth),
2038
self._decode_element(comment))
2041
def _handle_comment(self, comment):
2042
"""Deal with a comment."""
2045
start = self.indent_type
2046
if not comment.startswith('#'):
2047
start += self._a_to_u(' # ')
2048
return (start + comment)
2053
def write(self, outfile=None, section=None):
2055
Write the current ConfigObj as a file
2057
tekNico: FIXME: use StringIO instead of real files
2059
>>> filename = a.filename
2060
>>> a.filename = 'test.ini'
2062
>>> a.filename = filename
2063
>>> a == ConfigObj('test.ini', raise_errors=True)
2066
if self.indent_type is None:
2067
# this can be true if initialised from a dictionary
2068
self.indent_type = DEFAULT_INDENT_TYPE
2071
cs = self._a_to_u('#')
2072
csp = self._a_to_u('# ')
2074
int_val = self.interpolation
2075
self.interpolation = False
2077
for line in self.initial_comment:
2078
line = self._decode_element(line)
2079
stripped_line = line.strip()
2080
if stripped_line and not stripped_line.startswith(cs):
2084
indent_string = self.indent_type * section.depth
2085
for entry in (section.scalars + section.sections):
2086
if entry in section.defaults:
2087
# don't write out default values
2089
for comment_line in section.comments[entry]:
2090
comment_line = self._decode_element(comment_line.lstrip())
2091
if comment_line and not comment_line.startswith(cs):
2092
comment_line = csp + comment_line
2093
out.append(indent_string + comment_line)
2094
this_entry = section[entry]
2095
comment = self._handle_comment(section.inline_comments[entry])
2097
if isinstance(this_entry, dict):
2099
out.append(self._write_marker(
2104
out.extend(self.write(section=this_entry))
2106
out.append(self._write_line(
2113
for line in self.final_comment:
2114
line = self._decode_element(line)
2115
stripped_line = line.strip()
2116
if stripped_line and not stripped_line.startswith(cs):
2119
self.interpolation = int_val
2121
if section is not self:
2124
if (self.filename is None) and (outfile is None):
2125
# output a list of lines
2126
# might need to encode
2127
# NOTE: This will *screw* UTF16, each line will start with the BOM
2129
out = [l.encode(self.encoding) for l in out]
2130
if (self.BOM and ((self.encoding is None) or
2131
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2135
out[0] = BOM_UTF8 + out[0]
2138
# Turn the list to a string, joined with correct newlines
2139
newline = self.newlines or os.linesep
2140
output = self._a_to_u(newline).join(out)
2142
output = output.encode(self.encoding)
2143
if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2145
output = BOM_UTF8 + output
2147
if not output.endswith(newline):
2149
if outfile is not None:
2150
outfile.write(output)
2152
h = open(self.filename, 'wb')
2157
def validate(self, validator, preserve_errors=False, copy=False,
2160
Test the ConfigObj against a configspec.
2162
It uses the ``validator`` object from *validate.py*.
2164
To run ``validate`` on the current ConfigObj, call: ::
2166
test = config.validate(validator)
2168
(Normally having previously passed in the configspec when the ConfigObj
2169
was created - you can dynamically assign a dictionary of checks to the
2170
``configspec`` attribute of a section though).
2172
It returns ``True`` if everything passes, or a dictionary of
2173
pass/fails (True/False). If every member of a subsection passes, it
2174
will just have the value ``True``. (It also returns ``False`` if all
2177
In addition, it converts the values from strings to their native
2178
types if their checks pass (and ``stringify`` is set).
2180
If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2181
of a marking a fail with a ``False``, it will preserve the actual
2182
exception object. This can contain info about the reason for failure.
2183
For example the ``VdtValueTooSmallError`` indicates that the value
2184
supplied was too small. If a value (or section) is missing it will
2185
still be marked as ``False``.
2187
You must have the validate module to use ``preserve_errors=True``.
2189
You can then use the ``flatten_errors`` function to turn your nested
2190
results dictionary into a flattened list of failures - useful for
2191
displaying meaningful error messages.
2194
if self.configspec is None:
2195
raise ValueError('No configspec supplied.')
2197
# We do this once to remove a top level dependency on the validate module
2198
# Which makes importing configobj faster
2199
from validate import VdtMissingValue
2200
self._vdtMissingValue = VdtMissingValue
2203
spec_section = section.configspec
2204
if copy and getattr(section, '_configspec_initial_comment', None) is not None:
2205
section.initial_comment = section._configspec_initial_comment
2206
section.final_comment = section._configspec_final_comment
2207
section.encoding = section._configspec_encoding
2208
section.BOM = section._configspec_BOM
2209
section.newlines = section._configspec_newlines
2210
section.indent_type = section._configspec_indent_type
2212
if '__many__' in section.configspec:
2213
many = spec_section['__many__']
2214
# dynamically assign the configspecs
2215
# for the sections below
2216
for entry in section.sections:
2217
self._handle_repeat(section[entry], many)
2222
order = [k for k in section._order if k in spec_section]
2223
order += [k for k in spec_section if k not in order]
2225
if entry == '__many__':
2227
if (not entry in section.scalars) or (entry in section.defaults):
2229
# or entries from defaults
2232
if copy and not entry in section.scalars:
2234
section.comments[entry] = (
2235
section._configspec_comments.get(entry, []))
2236
section.inline_comments[entry] = (
2237
section._configspec_inline_comments.get(entry, ''))
2241
val = section[entry]
2243
check = validator.check(spec_section[entry],
2247
except validator.baseErrorClass, e:
2248
if not preserve_errors or isinstance(e, self._vdtMissingValue):
2251
# preserve the error
2257
section.default_values.pop(entry, None)
2258
except AttributeError:
2259
# For Python 2.2 compatibility
2261
del section.default_values[entry]
2265
if getattr(validator, 'get_default_value', None) is not None:
2267
section.default_values[entry] = validator.get_default_value(spec_section[entry])
2274
if self.stringify or missing:
2275
# if we are doing type conversion
2276
# or the value is a supplied default
2277
if not self.stringify:
2278
if isinstance(check, (list, tuple)):
2280
check = [self._str(item) for item in check]
2281
elif missing and check is None:
2282
# convert the None from a default to a ''
2285
check = self._str(check)
2286
if (check != val) or missing:
2287
section[entry] = check
2288
if not copy and missing and entry not in section.defaults:
2289
section.defaults.append(entry)
2290
# Missing sections will have been created as empty ones when the
2291
# configspec was read.
2292
for entry in section.sections:
2293
# FIXME: this means DEFAULT is not copied in copy mode
2294
if section is self and entry == 'DEFAULT':
2297
section.comments[entry] = section._cs_section_comments[entry]
2298
section.inline_comments[entry] = (
2299
section._cs_section_inline_comments[entry])
2300
check = self.validate(validator, preserve_errors=preserve_errors,
2301
copy=copy, section=section[entry])
2319
"""Clear ConfigObj instance and restore to 'freshly created' state."""
2322
# FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2323
# requires an empty dictionary
2324
self.configspec = None
2325
# Just to be sure ;-)
2326
self._original_configspec = None
2331
Reload a ConfigObj from file.
2333
This method raises a ``ReloadError`` if the ConfigObj doesn't have
2334
a filename attribute pointing to a file.
2336
if not isinstance(self.filename, StringTypes):
2339
filename = self.filename
2340
current_options = {}
2341
for entry in OPTION_DEFAULTS:
2342
if entry == 'configspec':
2344
current_options[entry] = getattr(self, entry)
2346
configspec = self._original_configspec
2347
current_options['configspec'] = configspec
2350
self._initialise(current_options)
2351
self._load(filename, configspec)
2355
class SimpleVal(object):
2358
Can be used to check that all members expected are present.
2360
To use it, provide a configspec with all your members in (the value given
2361
will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2362
method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2363
members are present, or a dictionary with True/False meaning
2364
present/missing. (Whole missing sections will be replaced with ``False``)
2368
self.baseErrorClass = ConfigObjError
2370
def check(self, check, member, missing=False):
2371
"""A dummy check method, always returns the value unchanged."""
2373
raise self.baseErrorClass()
2377
# Check / processing functions for options
2378
def flatten_errors(cfg, res, levels=None, results=None):
2380
An example function that will turn a nested dictionary of results
2381
(as returned by ``ConfigObj.validate``) into a flat list.
2383
``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2384
dictionary returned by ``validate``.
2386
(This is a recursive function, so you shouldn't use the ``levels`` or
2387
``results`` arguments - they are used by the function.
2389
Returns a list of keys that failed. Each member of the list is a tuple :
2392
([list of sections...], key, result)
2394
If ``validate`` was called with ``preserve_errors=False`` (the default)
2395
then ``result`` will always be ``False``.
2397
*list of sections* is a flattened list of sections that the key was found
2400
If the section was missing then key will be ``None``.
2402
If the value (or section) was missing then ``result`` will be ``False``.
2404
If ``validate`` was called with ``preserve_errors=True`` and a value
2405
was present, but failed the check, then ``result`` will be the exception
2406
object returned. You can use this as a string that describes the failure.
2408
For example *The value "3" is of the wrong type*.
2411
>>> vtor = validate.Validator()
2417
... another_option = Probably
2419
... another_option = True
2426
... option1 = boolean()
2427
... option2 = boolean()
2428
... option3 = boolean(default=Bad_value)
2430
... option1 = boolean()
2431
... option2 = boolean()
2432
... option3 = boolean(default=Bad_value)
2434
... another_option = boolean()
2436
... another_option = boolean()
2439
... value2 = integer
2440
... value3 = integer(0, 10)
2441
... [[[section3b-sub]]]
2444
... another_option = boolean()
2446
>>> cs = my_cfg.split('\\n')
2447
>>> ini = my_ini.split('\\n')
2448
>>> cfg = ConfigObj(ini, configspec=cs)
2449
>>> res = cfg.validate(vtor, preserve_errors=True)
2451
>>> for entry in flatten_errors(cfg, res):
2452
... section_list, key, error = entry
2453
... section_list.insert(0, '[root]')
2454
... if key is not None:
2455
... section_list.append(key)
2457
... section_list.append('[missing]')
2458
... section_string = ', '.join(section_list)
2459
... errors.append((section_string, ' = ', error))
2461
>>> for entry in errors:
2462
... print entry[0], entry[1], (entry[2] or 0)
2464
[root], option3 = the value "Bad_value" is of the wrong type.
2465
[root], section1, option2 = 0
2466
[root], section1, option3 = the value "Bad_value" is of the wrong type.
2467
[root], section2, another_option = the value "Probably" is of the wrong type.
2468
[root], section3, section3b, section3b-sub, [missing] = 0
2469
[root], section3, section3b, value2 = the value "a" is of the wrong type.
2470
[root], section3, section3b, value3 = the value "11" is too big.
2471
[root], section4, [missing] = 0
2480
results.append((levels[:], None, False))
2484
for (key, val) in res.items():
2487
if isinstance(cfg.get(key), dict):
2490
flatten_errors(cfg[key], val, levels, results)
2492
results.append((levels[:], key, val))
2501
"""*A programming language is a medium of expression.* - Paul Graham"""