2
# A config file reader/writer that supports nested sections in config files.
3
# Copyright (C) 2005-2010 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
25
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
28
# imported lazily to avoid startup performance hit if it isn't used
31
# A dictionary mapping BOM to
32
# the encoding to decode with, and what to set the
33
# encoding attribute to.
35
BOM_UTF8: ('utf_8', None),
36
BOM_UTF16_BE: ('utf16_be', 'utf_16'),
37
BOM_UTF16_LE: ('utf16_le', 'utf_16'),
38
BOM_UTF16: ('utf_16', 'utf_16'),
40
# All legal variants of the BOM codecs.
41
# TODO: the list of aliases is not meant to be exhaustive, is there a
48
'utf16_be': 'utf16_be',
49
'utf_16_be': 'utf16_be',
50
'utf-16be': 'utf16_be',
51
'utf16_le': 'utf16_le',
52
'utf_16_le': 'utf16_le',
53
'utf-16le': 'utf16_le',
61
# Map of encodings to the BOM to write.
65
'utf16_be': BOM_UTF16_BE,
66
'utf16_le': BOM_UTF16_LE,
71
def match_utf8(encoding):
72
return BOM_LIST.get(encoding.lower()) == 'utf_8'
75
# Quote strings used for writing values
79
wspace_plus = ' \r\n\v\t\'"'
83
# Sentinel for use in getattr calls to replace hasattr
92
for entry in iterable:
100
'DEFAULT_INDENT_TYPE',
101
'DEFAULT_INTERPOLATION',
109
'InterpolationError',
110
'InterpolationLoopError',
111
'MissingInterpolationOption',
112
'RepeatSectionError',
120
DEFAULT_INTERPOLATION = 'configparser'
121
DEFAULT_INDENT_TYPE = ' '
122
MAX_INTERPOL_DEPTH = 10
125
'interpolation': True,
126
'raise_errors': False,
128
'create_empty': False,
132
# option may be set to one of ('', ' ', '\t')
135
'default_encoding': None,
137
'write_empty_values': False,
147
p = compiler.parse(s)
148
return p.getChildren()[1].getChildren()[0].getChildren()[1]
151
class UnknownType(Exception):
155
class Builder(object):
158
m = getattr(self, 'build_' + o.__class__.__name__, None)
160
raise UnknownType(o.__class__.__name__)
163
def build_List(self, o):
164
return map(self.build, o.getChildren())
166
def build_Const(self, o):
169
def build_Dict(self, o):
171
i = iter(map(self.build, o.getChildren()))
176
def build_Tuple(self, o):
177
return tuple(self.build_List(o))
179
def build_Name(self, o):
184
if o.name == 'False':
188
raise UnknownType('Undefined Name')
190
def build_Add(self, o):
191
real, imag = map(self.build_Const, o.getChildren())
195
raise UnknownType('Add')
196
if not isinstance(imag, complex) or imag.real != 0.0:
197
raise UnknownType('Add')
200
def build_Getattr(self, o):
201
parent = self.build(o.expr)
202
return getattr(parent, o.attrname)
204
def build_UnarySub(self, o):
205
return -self.build_Const(o.getChildren()[0])
207
def build_UnaryAdd(self, o):
208
return self.build_Const(o.getChildren()[0])
217
return _builder.build(getObj(s))
221
class ConfigObjError(SyntaxError):
223
This is the base class for all errors that ConfigObj raises.
224
It is a subclass of SyntaxError.
226
def __init__(self, message='', line_number=None, line=''):
228
self.line_number = line_number
229
SyntaxError.__init__(self, message)
232
class NestingError(ConfigObjError):
234
This error indicates a level of nesting that doesn't match.
238
class ParseError(ConfigObjError):
240
This error indicates that a line is badly written.
241
It is neither a valid ``key = value`` line,
242
nor a valid section marker line.
246
class ReloadError(IOError):
248
A 'reload' operation failed.
249
This exception is a subclass of ``IOError``.
252
IOError.__init__(self, 'reload failed, filename is not set.')
255
class DuplicateError(ConfigObjError):
257
The keyword or section specified already exists.
261
class ConfigspecError(ConfigObjError):
263
An error occured whilst parsing a configspec.
267
class InterpolationError(ConfigObjError):
268
"""Base class for the two interpolation errors."""
271
class InterpolationLoopError(InterpolationError):
272
"""Maximum interpolation depth exceeded in string interpolation."""
274
def __init__(self, option):
275
InterpolationError.__init__(
277
'interpolation loop detected in value "%s".' % option)
280
class RepeatSectionError(ConfigObjError):
282
This error indicates additional sections in a section with a
283
``__many__`` (repeated) section.
287
class MissingInterpolationOption(InterpolationError):
288
"""A value specified for interpolation was missing."""
289
def __init__(self, option):
290
msg = 'missing option "%s" in interpolation.' % option
291
InterpolationError.__init__(self, msg)
294
class UnreprError(ConfigObjError):
295
"""An error parsing in unrepr mode."""
299
class InterpolationEngine(object):
301
A helper class to help perform string interpolation.
303
This class is an abstract base class; its descendants perform
307
# compiled regexp to use in self.interpolate()
308
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
311
def __init__(self, section):
312
# the Section instance that "owns" this engine
313
self.section = section
316
def interpolate(self, key, value):
318
if not self._cookie in value:
321
def recursive_interpolate(key, value, section, backtrail):
322
"""The function that does the actual work.
324
``value``: the string we're trying to interpolate.
325
``section``: the section in which that string was found
326
``backtrail``: a dict to keep track of where we've been,
327
to detect and prevent infinite recursion loops
329
This is similar to a depth-first-search algorithm.
331
# Have we been here already?
332
if (key, section.name) in backtrail:
333
# Yes - infinite loop detected
334
raise InterpolationLoopError(key)
335
# Place a marker on our backtrail so we won't come back here again
336
backtrail[(key, section.name)] = 1
338
# Now start the actual work
339
match = self._KEYCRE.search(value)
341
# The actual parsing of the match is implementation-dependent,
342
# so delegate to our helper function
343
k, v, s = self._parse_match(match)
345
# That's the signal that no further interpolation is needed
348
# Further interpolation may be needed to obtain final value
349
replacement = recursive_interpolate(k, v, s, backtrail)
350
# Replace the matched string with its final value
351
start, end = match.span()
352
value = ''.join((value[:start], replacement, value[end:]))
353
new_search_start = start + len(replacement)
354
# Pick up the next interpolation key, if any, for next time
355
# through the while loop
356
match = self._KEYCRE.search(value, new_search_start)
358
# Now safe to come back here again; remove marker from backtrail
359
del backtrail[(key, section.name)]
363
# Back in interpolate(), all we have to do is kick off the recursive
364
# function with appropriate starting values
365
value = recursive_interpolate(key, value, self.section, {})
369
def _fetch(self, key):
370
"""Helper function to fetch values from owning section.
372
Returns a 2-tuple: the value, and the section where it was found.
374
# switch off interpolation before we try and fetch anything !
375
save_interp = self.section.main.interpolation
376
self.section.main.interpolation = False
378
# Start at section that "owns" this InterpolationEngine
379
current_section = self.section
381
# try the current section first
382
val = current_section.get(key)
383
if val is not None and not isinstance(val, Section):
386
val = current_section.get('DEFAULT', {}).get(key)
387
if val is not None and not isinstance(val, Section):
389
# move up to parent and try again
390
# top-level's parent is itself
391
if current_section.parent is current_section:
392
# reached top level, time to give up
394
current_section = current_section.parent
396
# restore interpolation to previous value before returning
397
self.section.main.interpolation = save_interp
399
raise MissingInterpolationOption(key)
400
return val, current_section
403
def _parse_match(self, match):
404
"""Implementation-dependent helper function.
406
Will be passed a match object corresponding to the interpolation
407
key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
408
key in the appropriate config file section (using the ``_fetch()``
409
helper function) and return a 3-tuple: (key, value, section)
411
``key`` is the name of the key we're looking for
412
``value`` is the value found for that key
413
``section`` is a reference to the section where it was found
415
``key`` and ``section`` should be None if no further
416
interpolation should be performed on the resulting value
417
(e.g., if we interpolated "$$" and returned "$").
419
raise NotImplementedError()
423
class ConfigParserInterpolation(InterpolationEngine):
424
"""Behaves like ConfigParser."""
426
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
428
def _parse_match(self, match):
430
value, section = self._fetch(key)
431
return key, value, section
435
class TemplateInterpolation(InterpolationEngine):
436
"""Behaves like string.Template."""
439
_KEYCRE = re.compile(r"""
441
(?P<escaped>\$) | # Two $ signs
442
(?P<named>[_a-z][_a-z0-9]*) | # $name format
443
{(?P<braced>[^}]*)} # ${name} format
445
""", re.IGNORECASE | re.VERBOSE)
447
def _parse_match(self, match):
448
# Valid name (in or out of braces): fetch value from section
449
key = match.group('named') or match.group('braced')
451
value, section = self._fetch(key)
452
return key, value, section
453
# Escaped delimiter (e.g., $$): return single delimiter
454
if match.group('escaped') is not None:
455
# Return None for key and section to indicate it's time to stop
456
return None, self._delimiter, None
457
# Anything else: ignore completely, just return it unchanged
458
return None, match.group(), None
461
interpolation_engines = {
462
'configparser': ConfigParserInterpolation,
463
'template': TemplateInterpolation,
467
def __newobj__(cls, *args):
469
return cls.__new__(cls, *args)
473
A dictionary-like object that represents a section in a config file.
475
It does string interpolation if the 'interpolation' attribute
476
of the 'main' object is set to True.
478
Interpolation is tried first from this object, then from the 'DEFAULT'
479
section of this object, next from the parent and its 'DEFAULT' section,
480
and so on until the main object is reached.
482
A Section will behave like an ordered dictionary - following the
483
order of the ``scalars`` and ``sections`` attributes.
484
You can use this to change the order of members.
486
Iteration follows the order: scalars, then sections.
490
def __setstate__(self, state):
491
dict.update(self, state[0])
492
self.__dict__.update(state[1])
494
def __reduce__(self):
495
state = (dict(self), self.__dict__)
496
return (__newobj__, (self.__class__,), state)
499
def __init__(self, parent, depth, main, indict=None, name=None):
501
* parent is the section above
502
* depth is the depth level of this section
503
* main is the main ConfigObj
504
* indict is a dictionary to initialise the section with
509
# used for nesting level *and* interpolation
511
# used for the interpolation attribute
513
# level of nesting depth of this Section
515
# purely for information
519
# we do this explicitly so that __setitem__ is used properly
520
# (rather than just passing to ``dict.__init__``)
521
for entry, value in indict.iteritems():
525
def _initialise(self):
526
# the sequence of scalar values in this Section
528
# the sequence of sections in this Section
532
self.inline_comments = {}
534
self.configspec = None
537
self.default_values = {}
538
self.extra_values = []
539
self._created = False
542
def _interpolate(self, key, value):
544
# do we already have an interpolation engine?
545
engine = self._interpolation_engine
546
except AttributeError:
547
# not yet: first time running _interpolate(), so pick the engine
548
name = self.main.interpolation
549
if name == True: # note that "if name:" would be incorrect here
550
# backwards-compatibility: interpolation=True means use default
551
name = DEFAULT_INTERPOLATION
552
name = name.lower() # so that "Template", "template", etc. all work
553
class_ = interpolation_engines.get(name, None)
555
# invalid value for self.main.interpolation
556
self.main.interpolation = False
559
# save reference to engine so we don't have to do this again
560
engine = self._interpolation_engine = class_(self)
561
# let the engine do the actual work
562
return engine.interpolate(key, value)
565
def __getitem__(self, key):
566
"""Fetch the item and do string interpolation."""
567
val = dict.__getitem__(self, key)
568
if self.main.interpolation:
569
if isinstance(val, basestring):
570
return self._interpolate(key, val)
571
if isinstance(val, list):
573
if isinstance(entry, basestring):
574
return self._interpolate(key, entry)
576
new = [_check(entry) for entry in val]
582
def __setitem__(self, key, value, unrepr=False):
584
Correctly set a value.
586
Making dictionary values Section instances.
587
(We have to special case 'Section' instances - which are also dicts)
589
Keys must be strings.
590
Values need only be strings (or lists of strings) if
591
``main.stringify`` is set.
593
``unrepr`` must be set when setting a value to a dictionary, without
594
creating a new sub-section.
596
if not isinstance(key, basestring):
597
raise ValueError('The key "%s" is not a string.' % key)
600
if key not in self.comments:
601
self.comments[key] = []
602
self.inline_comments[key] = ''
603
# remove the entry from defaults
604
if key in self.defaults:
605
self.defaults.remove(key)
607
if isinstance(value, Section):
609
self.sections.append(key)
610
dict.__setitem__(self, key, value)
611
elif isinstance(value, dict) and not unrepr:
612
# First create the new depth level,
613
# then create the section
615
self.sections.append(key)
616
new_depth = self.depth + 1
628
self.scalars.append(key)
629
if not self.main.stringify:
630
if isinstance(value, basestring):
632
elif isinstance(value, (list, tuple)):
634
if not isinstance(entry, basestring):
635
raise TypeError('Value is not a string "%s".' % entry)
637
raise TypeError('Value is not a string "%s".' % value)
638
dict.__setitem__(self, key, value)
641
def __delitem__(self, key):
642
"""Remove items from the sequence when deleting."""
643
dict. __delitem__(self, key)
644
if key in self.scalars:
645
self.scalars.remove(key)
647
self.sections.remove(key)
648
del self.comments[key]
649
del self.inline_comments[key]
652
def get(self, key, default=None):
653
"""A version of ``get`` that doesn't bypass string interpolation."""
660
def update(self, indict):
662
A version of update that uses our ``__setitem__``.
665
self[entry] = indict[entry]
668
def pop(self, key, default=MISSING):
670
'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
671
If key is not found, d is returned if given, otherwise KeyError is raised'
676
if default is MISSING:
685
"""Pops the first (key,val)"""
686
sequence = (self.scalars + self.sections)
688
raise KeyError(": 'popitem(): dictionary is empty'")
697
A version of clear that also affects scalars/sections
698
Also clears comments and configspec.
700
Leaves other attributes alone :
701
depth/main/parent are not affected
707
self.inline_comments = {}
708
self.configspec = None
710
self.extra_values = []
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)"""
759
except MissingInterpolationOption:
760
return dict.__getitem__(self, key)
761
return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(_getval(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
ConfigObj({'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.
878
.. admonition:: caution
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
ConfigObj({'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
ConfigObj({'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 as_bool(self, key):
942
Accepts a key as input. The corresponding value must be a string or
943
the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
944
retain compatibility with Python 2.2.
946
If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
949
If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
952
``as_bool`` is not case sensitive.
954
Any other input will raise a ``ValueError``.
959
Traceback (most recent call last):
960
ValueError: Value "fish" is neither True nor False
975
if not isinstance(val, basestring):
976
# TODO: Why do we raise a KeyError here?
979
return self.main._bools[val.lower()]
981
raise ValueError('Value "%s" is neither True nor False' % val)
984
def as_int(self, key):
986
A convenience method which coerces the specified value to an integer.
988
If the value is an invalid literal for ``int``, a ``ValueError`` will
994
Traceback (most recent call last):
995
ValueError: invalid literal for int() with base 10: 'fish'
1001
Traceback (most recent call last):
1002
ValueError: invalid literal for int() with base 10: '3.2'
1004
return int(self[key])
1007
def as_float(self, key):
1009
A convenience method which coerces the specified value to a float.
1011
If the value is an invalid literal for ``float``, a ``ValueError`` will
1017
Traceback (most recent call last):
1018
ValueError: invalid literal for float(): fish
1026
return float(self[key])
1029
def as_list(self, key):
1031
A convenience method which fetches the specified value, guaranteeing
1046
if isinstance(result, (tuple, list)):
1051
def restore_default(self, key):
1053
Restore (and return) default value for the specified key.
1055
This method will only work for a ConfigObj that was created
1056
with a configspec and has been validated.
1058
If there is no default value for this key, ``KeyError`` is raised.
1060
default = self.default_values[key]
1061
dict.__setitem__(self, key, default)
1062
if key not in self.defaults:
1063
self.defaults.append(key)
1067
def restore_defaults(self):
1069
Recursively restore default values to all members
1072
This method will only work for a ConfigObj that was created
1073
with a configspec and has been validated.
1075
It doesn't delete or modify entries without default values.
1077
for key in self.default_values:
1078
self.restore_default(key)
1080
for section in self.sections:
1081
self[section].restore_defaults()
1084
class ConfigObj(Section):
1085
"""An object to read, create, and write config files."""
1087
_keyword = re.compile(r'''^ # line start
1090
(?:".*?")| # double quotes
1091
(?:'.*?')| # single quotes
1092
(?:[^'"=].*?) # no quotes
1095
(.*) # value (including list values and comments)
1100
_sectionmarker = re.compile(r'''^
1101
(\s*) # 1: indentation
1102
((?:\[\s*)+) # 2: section marker open
1103
( # 3: section name open
1104
(?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1105
(?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1106
(?:[^'"\s].*?) # at least one non-space unquoted
1107
) # section name close
1108
((?:\s*\])+) # 4: section marker close
1109
\s*(\#.*)? # 5: optional comment
1113
# this regexp pulls list values out as a single string
1114
# or single values and comments
1115
# FIXME: this regex adds a '' to the end of comma terminated lists
1116
# workaround in ``_handle_value``
1117
_valueexp = re.compile(r'''^
1123
(?:".*?")| # double quotes
1124
(?:'.*?')| # single quotes
1125
(?:[^'",\#][^,\#]*?) # unquoted
1128
)* # match all list items ending in a comma (if any)
1131
(?:".*?")| # double quotes
1132
(?:'.*?')| # single quotes
1133
(?:[^'",\#\s][^,]*?)| # unquoted
1134
(?:(?<!,)) # Empty value
1135
)? # last item in a list - or string value
1137
(,) # alternatively a single comma - empty list
1139
\s*(\#.*)? # optional comment
1143
# use findall to get the members of a list value
1144
_listvalueexp = re.compile(r'''
1146
(?:".*?")| # double quotes
1147
(?:'.*?')| # single quotes
1148
(?:[^'",\#]?.*?) # unquoted
1154
# this regexp is used for the value
1155
# when lists are switched off
1156
_nolistvalue = re.compile(r'''^
1158
(?:".*?")| # double quotes
1159
(?:'.*?')| # single quotes
1160
(?:[^'"\#].*?)| # unquoted
1163
\s*(\#.*)? # optional comment
1167
# regexes for finding triple quoted values on one line
1168
_single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1169
_single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1170
_multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1171
_multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1174
"'''": (_single_line_single, _multi_line_single),
1175
'"""': (_single_line_double, _multi_line_double),
1178
# Used by the ``istrue`` Section method
1180
'yes': True, 'no': False,
1181
'on': True, 'off': False,
1182
'1': True, '0': False,
1183
'true': True, 'false': False,
1187
def __init__(self, infile=None, options=None, configspec=None, encoding=None,
1188
interpolation=True, raise_errors=False, list_values=True,
1189
create_empty=False, file_error=False, stringify=True,
1190
indent_type=None, default_encoding=None, unrepr=False,
1191
write_empty_values=False, _inspec=False):
1193
Parse a config file or create a config file object.
1195
``ConfigObj(infile=None, configspec=None, encoding=None,
1196
interpolation=True, raise_errors=False, list_values=True,
1197
create_empty=False, file_error=False, stringify=True,
1198
indent_type=None, default_encoding=None, unrepr=False,
1199
write_empty_values=False, _inspec=False)``
1201
self._inspec = _inspec
1202
# init the superclass
1203
Section.__init__(self, self, 0, self)
1205
infile = infile or []
1207
_options = {'configspec': configspec,
1208
'encoding': encoding, 'interpolation': interpolation,
1209
'raise_errors': raise_errors, 'list_values': list_values,
1210
'create_empty': create_empty, 'file_error': file_error,
1211
'stringify': stringify, 'indent_type': indent_type,
1212
'default_encoding': default_encoding, 'unrepr': unrepr,
1213
'write_empty_values': write_empty_values}
1219
warnings.warn('Passing in an options dictionary to ConfigObj() is '
1220
'deprecated. Use **options instead.',
1221
DeprecationWarning, stacklevel=2)
1223
# TODO: check the values too.
1224
for entry in options:
1225
if entry not in OPTION_DEFAULTS:
1226
raise TypeError('Unrecognised option "%s".' % entry)
1227
for entry, value in OPTION_DEFAULTS.items():
1228
if entry not in options:
1229
options[entry] = value
1230
keyword_value = _options[entry]
1231
if value != keyword_value:
1232
options[entry] = keyword_value
1234
# XXXX this ignores an explicit list_values = True in combination
1235
# with _inspec. The user should *never* do that anyway, but still...
1237
options['list_values'] = False
1239
self._initialise(options)
1240
configspec = options['configspec']
1241
self._original_configspec = configspec
1242
self._load(infile, configspec)
1245
def _load(self, infile, configspec):
1246
if isinstance(infile, basestring):
1247
self.filename = infile
1248
if os.path.isfile(infile):
1249
h = open(infile, 'rb')
1250
infile = h.read() or []
1252
elif self.file_error:
1253
# raise an error if the file doesn't exist
1254
raise IOError('Config file not found: "%s".' % self.filename)
1256
# file doesn't already exist
1257
if self.create_empty:
1258
# this is a good test that the filename specified
1259
# isn't impossible - like on a non-existent device
1260
h = open(infile, 'w')
1265
elif isinstance(infile, (list, tuple)):
1266
infile = list(infile)
1268
elif isinstance(infile, dict):
1270
# the Section class handles creating subsections
1271
if isinstance(infile, ConfigObj):
1272
# get a copy of our ConfigObj
1273
def set_section(in_section, this_section):
1274
for entry in in_section.scalars:
1275
this_section[entry] = in_section[entry]
1276
for section in in_section.sections:
1277
this_section[section] = {}
1278
set_section(in_section[section], this_section[section])
1279
set_section(infile, self)
1282
for entry in infile:
1283
self[entry] = infile[entry]
1286
if configspec is not None:
1287
self._handle_configspec(configspec)
1289
self.configspec = None
1292
elif getattr(infile, 'read', MISSING) is not MISSING:
1293
# This supports file like objects
1294
infile = infile.read() or []
1295
# needs splitting into lines - but needs doing *after* decoding
1296
# in case it's not an 8 bit encoding
1298
raise TypeError('infile must be a filename, file like object, or list of lines.')
1301
# don't do it for the empty ConfigObj
1302
infile = self._handle_bom(infile)
1303
# infile is now *always* a list
1305
# Set the newlines attribute (first line ending it finds)
1306
# and strip trailing '\n' or '\r' from lines
1308
if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1310
for end in ('\r\n', '\n', '\r'):
1311
if line.endswith(end):
1316
infile = [line.rstrip('\r\n') for line in infile]
1319
# if we had any errors, now is the time to raise them
1321
info = "at line %s." % self._errors[0].line_number
1322
if len(self._errors) > 1:
1323
msg = "Parsing failed with several errors.\nFirst error %s" % info
1324
error = ConfigObjError(msg)
1326
error = self._errors[0]
1327
# set the errors attribute; it's a list of tuples:
1328
# (error_type, message, line_number)
1329
error.errors = self._errors
1330
# set the config attribute
1333
# delete private attributes
1336
if configspec is None:
1337
self.configspec = None
1339
self._handle_configspec(configspec)
1342
def _initialise(self, options=None):
1344
options = OPTION_DEFAULTS
1346
# initialise a few variables
1347
self.filename = None
1349
self.raise_errors = options['raise_errors']
1350
self.interpolation = options['interpolation']
1351
self.list_values = options['list_values']
1352
self.create_empty = options['create_empty']
1353
self.file_error = options['file_error']
1354
self.stringify = options['stringify']
1355
self.indent_type = options['indent_type']
1356
self.encoding = options['encoding']
1357
self.default_encoding = options['default_encoding']
1359
self.newlines = None
1360
self.write_empty_values = options['write_empty_values']
1361
self.unrepr = options['unrepr']
1363
self.initial_comment = []
1364
self.final_comment = []
1365
self.configspec = None
1368
self.list_values = False
1370
# Clear section attributes as well
1371
Section._initialise(self)
1378
except MissingInterpolationOption:
1379
return dict.__getitem__(self, key)
1380
return ('ConfigObj({%s})' %
1381
', '.join([('%s: %s' % (repr(key), repr(_getval(key))))
1382
for key in (self.scalars + self.sections)]))
1385
def _handle_bom(self, infile):
1387
Handle any BOM, and decode if necessary.
1389
If an encoding is specified, that *must* be used - but the BOM should
1390
still be removed (and the BOM attribute set).
1392
(If the encoding is wrongly specified, then a BOM for an alternative
1393
encoding won't be discovered or removed.)
1395
If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1396
removed. The BOM attribute will be set. UTF16 will be decoded to
1399
NOTE: This method must not be called with an empty ``infile``.
1401
Specifying the *wrong* encoding is likely to cause a
1402
``UnicodeDecodeError``.
1404
``infile`` must always be returned as a list of lines, but may be
1405
passed in as a single string.
1407
if ((self.encoding is not None) and
1408
(self.encoding.lower() not in BOM_LIST)):
1409
# No need to check for a BOM
1410
# the encoding specified doesn't have one
1412
return self._decode(infile, self.encoding)
1414
if isinstance(infile, (list, tuple)):
1418
if self.encoding is not None:
1419
# encoding explicitly supplied
1420
# And it could have an associated BOM
1421
# TODO: if encoding is just UTF16 - we ought to check for both
1422
# TODO: big endian and little endian versions.
1423
enc = BOM_LIST[self.encoding.lower()]
1425
# For UTF16 we try big endian and little endian
1426
for BOM, (encoding, final_encoding) in BOMS.items():
1427
if not final_encoding:
1430
if infile.startswith(BOM):
1433
# Don't need to remove BOM
1434
return self._decode(infile, encoding)
1436
# If we get this far, will *probably* raise a DecodeError
1437
# As it doesn't appear to start with a BOM
1438
return self._decode(infile, self.encoding)
1442
if not line.startswith(BOM):
1443
return self._decode(infile, self.encoding)
1445
newline = line[len(BOM):]
1448
if isinstance(infile, (list, tuple)):
1453
return self._decode(infile, self.encoding)
1455
# No encoding specified - so we need to check for UTF8/UTF16
1456
for BOM, (encoding, final_encoding) in BOMS.items():
1457
if not line.startswith(BOM):
1461
self.encoding = final_encoding
1462
if not final_encoding:
1466
newline = line[len(BOM):]
1467
if isinstance(infile, (list, tuple)):
1471
# UTF8 - don't decode
1472
if isinstance(infile, basestring):
1473
return infile.splitlines(True)
1476
# UTF16 - have to decode
1477
return self._decode(infile, encoding)
1479
# No BOM discovered and no encoding specified, just return
1480
if isinstance(infile, basestring):
1481
# infile read from a file will be a single string
1482
return infile.splitlines(True)
1486
def _a_to_u(self, aString):
1487
"""Decode ASCII strings to unicode if a self.encoding is specified."""
1489
return aString.decode('ascii')
1494
def _decode(self, infile, encoding):
1496
Decode infile to unicode. Using the specified encoding.
1498
if is a string, it also needs converting to a list.
1500
if isinstance(infile, basestring):
1502
# NOTE: Could raise a ``UnicodeDecodeError``
1503
return infile.decode(encoding).splitlines(True)
1504
for i, line in enumerate(infile):
1505
if not isinstance(line, unicode):
1506
# NOTE: The isinstance test here handles mixed lists of unicode/string
1507
# NOTE: But the decode will break on any non-string values
1508
# NOTE: Or could raise a ``UnicodeDecodeError``
1509
infile[i] = line.decode(encoding)
1513
def _decode_element(self, line):
1514
"""Decode element to unicode if necessary."""
1515
if not self.encoding:
1517
if isinstance(line, str) and self.default_encoding:
1518
return line.decode(self.default_encoding)
1522
def _str(self, value):
1524
Used by ``stringify`` within validate, to turn non-string values
1527
if not isinstance(value, basestring):
1533
def _parse(self, infile):
1534
"""Actually parse the config file."""
1535
temp_list_values = self.list_values
1537
self.list_values = False
1542
maxline = len(infile) - 1
1544
reset_comment = False
1546
while cur_index < maxline:
1550
line = infile[cur_index]
1551
sline = line.strip()
1552
# do we have anything on the line ?
1553
if not sline or sline.startswith('#'):
1554
reset_comment = False
1555
comment_list.append(line)
1559
# preserve initial comment
1560
self.initial_comment = comment_list
1564
reset_comment = True
1565
# first we check if it's a section marker
1566
mat = self._sectionmarker.match(line)
1569
(indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1570
if indent and (self.indent_type is None):
1571
self.indent_type = indent
1572
cur_depth = sect_open.count('[')
1573
if cur_depth != sect_close.count(']'):
1574
self._handle_error("Cannot compute the section depth at line %s.",
1575
NestingError, infile, cur_index)
1578
if cur_depth < this_section.depth:
1579
# the new section is dropping back to a previous level
1581
parent = self._match_depth(this_section,
1584
self._handle_error("Cannot compute nesting level at line %s.",
1585
NestingError, infile, cur_index)
1587
elif cur_depth == this_section.depth:
1588
# the new section is a sibling of the current section
1589
parent = this_section.parent
1590
elif cur_depth == this_section.depth + 1:
1591
# the new section is a child the current section
1592
parent = this_section
1594
self._handle_error("Section too nested at line %s.",
1595
NestingError, infile, cur_index)
1597
sect_name = self._unquote(sect_name)
1598
if sect_name in parent:
1599
self._handle_error('Duplicate section name at line %s.',
1600
DuplicateError, infile, cur_index)
1603
# create the new section
1604
this_section = Section(
1609
parent[sect_name] = this_section
1610
parent.inline_comments[sect_name] = comment
1611
parent.comments[sect_name] = comment_list
1614
# it's not a section marker,
1615
# so it should be a valid ``key = value`` line
1616
mat = self._keyword.match(line)
1618
# it neither matched as a keyword
1619
# or a section marker
1621
'Invalid line at line "%s".',
1622
ParseError, infile, cur_index)
1624
# is a keyword value
1625
# value will include any inline comment
1626
(indent, key, value) = mat.groups()
1627
if indent and (self.indent_type is None):
1628
self.indent_type = indent
1629
# check for a multiline value
1630
if value[:3] in ['"""', "'''"]:
1632
value, comment, cur_index = self._multiline(
1633
value, infile, cur_index, maxline)
1636
'Parse error in value at line %s.',
1637
ParseError, infile, cur_index)
1643
value = unrepr(value)
1644
except Exception, e:
1645
if type(e) == UnknownType:
1646
msg = 'Unknown name or type in value at line %s.'
1648
msg = 'Parse error in value at line %s.'
1649
self._handle_error(msg, UnreprError, infile,
1656
value = unrepr(value)
1657
except Exception, e:
1658
if isinstance(e, UnknownType):
1659
msg = 'Unknown name or type in value at line %s.'
1661
msg = 'Parse error in value at line %s.'
1662
self._handle_error(msg, UnreprError, infile,
1666
# extract comment and lists
1668
(value, comment) = self._handle_value(value)
1671
'Parse error in value at line %s.',
1672
ParseError, infile, cur_index)
1675
key = self._unquote(key)
1676
if key in this_section:
1678
'Duplicate keyword name at line %s.',
1679
DuplicateError, infile, cur_index)
1682
# we set unrepr because if we have got this far we will never
1683
# be creating a new section
1684
this_section.__setitem__(key, value, unrepr=True)
1685
this_section.inline_comments[key] = comment
1686
this_section.comments[key] = comment_list
1689
if self.indent_type is None:
1690
# no indentation used, set the type accordingly
1691
self.indent_type = ''
1693
# preserve the final comment
1694
if not self and not self.initial_comment:
1695
self.initial_comment = comment_list
1696
elif not reset_comment:
1697
self.final_comment = comment_list
1698
self.list_values = temp_list_values
1701
def _match_depth(self, sect, depth):
1703
Given a section and a depth level, walk back through the sections
1704
parents to see if the depth level matches a previous section.
1706
Return a reference to the right section,
1707
or raise a SyntaxError.
1709
while depth < sect.depth:
1710
if sect is sect.parent:
1711
# we've reached the top level already
1714
if sect.depth == depth:
1716
# shouldn't get here
1720
def _handle_error(self, text, ErrorClass, infile, cur_index):
1722
Handle an error according to the error settings.
1724
Either raise the error or store it.
1725
The error will have occured at ``cur_index``
1727
line = infile[cur_index]
1729
message = text % cur_index
1730
error = ErrorClass(message, cur_index, line)
1731
if self.raise_errors:
1732
# raise the error - parsing stops here
1735
# reraise when parsing has finished
1736
self._errors.append(error)
1739
def _unquote(self, value):
1740
"""Return an unquoted version of a value"""
1742
# should only happen during parsing of lists
1744
if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1749
def _quote(self, value, multiline=True):
1751
Return a safely quoted version of a value.
1753
Raise a ConfigObjError if the value cannot be safely quoted.
1754
If multiline is ``True`` (default) then use triple quotes
1757
* Don't quote values that don't need it.
1758
* Recursively quote members of a list and return a comma joined list.
1759
* Multiline is ``False`` for lists.
1760
* Obey list syntax for empty and single member lists.
1762
If ``list_values=False`` then the value is only quoted if it contains
1763
a ``\\n`` (is multiline) or '#'.
1765
If ``write_empty_values`` is set, and the value is an empty string, it
1768
if multiline and self.write_empty_values and value == '':
1769
# Only if multiline is set, so that it is used for values not
1770
# keys, and not values that are part of a list
1773
if multiline and isinstance(value, (list, tuple)):
1776
elif len(value) == 1:
1777
return self._quote(value[0], multiline=False) + ','
1778
return ', '.join([self._quote(val, multiline=False)
1780
if not isinstance(value, basestring):
1784
raise TypeError('Value "%s" is not a string.' % value)
1789
no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1790
need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1791
hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1792
check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1794
if check_for_single:
1795
if not self.list_values:
1796
# we don't quote if ``list_values=False``
1798
# for normal values either single or double quotes will do
1800
# will only happen if multiline is off - e.g. '\n' in key
1801
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1802
elif ((value[0] not in wspace_plus) and
1803
(value[-1] not in wspace_plus) and
1804
(',' not in value)):
1807
quot = self._get_single_quote(value)
1809
# if value has '\n' or "'" *and* '"', it will need triple quotes
1810
quot = self._get_triple_quote(value)
1812
if quot == noquot and '#' in value and self.list_values:
1813
quot = self._get_single_quote(value)
1818
def _get_single_quote(self, value):
1819
if ("'" in value) and ('"' in value):
1820
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1828
def _get_triple_quote(self, value):
1829
if (value.find('"""') != -1) and (value.find("'''") != -1):
1830
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1831
if value.find('"""') == -1:
1838
def _handle_value(self, value):
1840
Given a value string, unquote, remove comment,
1841
handle lists. (including empty and single member lists)
1844
# Parsing a configspec so don't handle comments
1846
# do we look for lists in values ?
1847
if not self.list_values:
1848
mat = self._nolistvalue.match(value)
1851
# NOTE: we don't unquote here
1854
mat = self._valueexp.match(value)
1856
# the value is badly constructed, probably badly quoted,
1857
# or an invalid list
1859
(list_values, single, empty_list, comment) = mat.groups()
1860
if (list_values == '') and (single is None):
1861
# change this if you want to accept empty values
1863
# NOTE: note there is no error handling from here if the regex
1864
# is wrong: then incorrect values will slip through
1865
if empty_list is not None:
1866
# the single comma - meaning an empty list
1867
return ([], comment)
1868
if single is not None:
1869
# handle empty values
1870
if list_values and not single:
1871
# FIXME: the '' is a workaround because our regex now matches
1872
# '' at the end of a list if it has a trailing comma
1875
single = single or '""'
1876
single = self._unquote(single)
1877
if list_values == '':
1879
return (single, comment)
1880
the_list = self._listvalueexp.findall(list_values)
1881
the_list = [self._unquote(val) for val in the_list]
1882
if single is not None:
1883
the_list += [single]
1884
return (the_list, comment)
1887
def _multiline(self, value, infile, cur_index, maxline):
1888
"""Extract the value, where we are in a multiline situation."""
1890
newvalue = value[3:]
1891
single_line = self._triple_quote[quot][0]
1892
multi_line = self._triple_quote[quot][1]
1893
mat = single_line.match(value)
1895
retval = list(mat.groups())
1896
retval.append(cur_index)
1898
elif newvalue.find(quot) != -1:
1899
# somehow the triple quote is missing
1902
while cur_index < maxline:
1905
line = infile[cur_index]
1906
if line.find(quot) == -1:
1909
# end of multiline, process it
1912
# we've got to the end of the config, oops...
1914
mat = multi_line.match(line)
1916
# a badly formed line
1918
(value, comment) = mat.groups()
1919
return (newvalue + value, comment, cur_index)
1922
def _handle_configspec(self, configspec):
1923
"""Parse the configspec."""
1924
# FIXME: Should we check that the configspec was created with the
1925
# correct settings ? (i.e. ``list_values=False``)
1926
if not isinstance(configspec, ConfigObj):
1928
configspec = ConfigObj(configspec,
1932
except ConfigObjError, e:
1933
# FIXME: Should these errors have a reference
1934
# to the already parsed ConfigObj ?
1935
raise ConfigspecError('Parsing configspec failed: %s' % e)
1937
raise IOError('Reading configspec failed: %s' % e)
1939
self.configspec = configspec
1943
def _set_configspec(self, section, copy):
1945
Called by validate. Handles setting the configspec on subsections
1946
including sections to be validated by __many__
1948
configspec = section.configspec
1949
many = configspec.get('__many__')
1950
if isinstance(many, dict):
1951
for entry in section.sections:
1952
if entry not in configspec:
1953
section[entry].configspec = many
1955
for entry in configspec.sections:
1956
if entry == '__many__':
1958
if entry not in section:
1960
section[entry]._created = True
1963
section.comments[entry] = configspec.comments.get(entry, [])
1964
section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1966
# Could be a scalar when we expect a section
1967
if isinstance(section[entry], Section):
1968
section[entry].configspec = configspec[entry]
1971
def _write_line(self, indent_string, entry, this_entry, comment):
1972
"""Write an individual line, for the write method"""
1973
# NOTE: the calls to self._quote here handles non-StringType values.
1975
val = self._decode_element(self._quote(this_entry))
1977
val = repr(this_entry)
1978
return '%s%s%s%s%s' % (indent_string,
1979
self._decode_element(self._quote(entry, multiline=False)),
1980
self._a_to_u(' = '),
1982
self._decode_element(comment))
1985
def _write_marker(self, indent_string, depth, entry, comment):
1986
"""Write a section marker line"""
1987
return '%s%s%s%s%s' % (indent_string,
1988
self._a_to_u('[' * depth),
1989
self._quote(self._decode_element(entry), multiline=False),
1990
self._a_to_u(']' * depth),
1991
self._decode_element(comment))
1994
def _handle_comment(self, comment):
1995
"""Deal with a comment."""
1998
start = self.indent_type
1999
if not comment.startswith('#'):
2000
start += self._a_to_u(' # ')
2001
return (start + comment)
2006
def write(self, outfile=None, section=None):
2008
Write the current ConfigObj as a file
2010
tekNico: FIXME: use StringIO instead of real files
2012
>>> filename = a.filename
2013
>>> a.filename = 'test.ini'
2015
>>> a.filename = filename
2016
>>> a == ConfigObj('test.ini', raise_errors=True)
2019
>>> os.remove('test.ini')
2021
if self.indent_type is None:
2022
# this can be true if initialised from a dictionary
2023
self.indent_type = DEFAULT_INDENT_TYPE
2026
cs = self._a_to_u('#')
2027
csp = self._a_to_u('# ')
2029
int_val = self.interpolation
2030
self.interpolation = False
2032
for line in self.initial_comment:
2033
line = self._decode_element(line)
2034
stripped_line = line.strip()
2035
if stripped_line and not stripped_line.startswith(cs):
2039
indent_string = self.indent_type * section.depth
2040
for entry in (section.scalars + section.sections):
2041
if entry in section.defaults:
2042
# don't write out default values
2044
for comment_line in section.comments[entry]:
2045
comment_line = self._decode_element(comment_line.lstrip())
2046
if comment_line and not comment_line.startswith(cs):
2047
comment_line = csp + comment_line
2048
out.append(indent_string + comment_line)
2049
this_entry = section[entry]
2050
comment = self._handle_comment(section.inline_comments[entry])
2052
if isinstance(this_entry, dict):
2054
out.append(self._write_marker(
2059
out.extend(self.write(section=this_entry))
2061
out.append(self._write_line(
2068
for line in self.final_comment:
2069
line = self._decode_element(line)
2070
stripped_line = line.strip()
2071
if stripped_line and not stripped_line.startswith(cs):
2074
self.interpolation = int_val
2076
if section is not self:
2079
if (self.filename is None) and (outfile is None):
2080
# output a list of lines
2081
# might need to encode
2082
# NOTE: This will *screw* UTF16, each line will start with the BOM
2084
out = [l.encode(self.encoding) for l in out]
2085
if (self.BOM and ((self.encoding is None) or
2086
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2090
out[0] = BOM_UTF8 + out[0]
2093
# Turn the list to a string, joined with correct newlines
2094
newline = self.newlines or os.linesep
2095
if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w'
2096
and sys.platform == 'win32' and newline == '\r\n'):
2097
# Windows specific hack to avoid writing '\r\r\n'
2099
output = self._a_to_u(newline).join(out)
2101
output = output.encode(self.encoding)
2102
if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2104
output = BOM_UTF8 + output
2106
if not output.endswith(newline):
2108
if outfile is not None:
2109
outfile.write(output)
2111
h = open(self.filename, 'wb')
2116
def validate(self, validator, preserve_errors=False, copy=False,
2119
Test the ConfigObj against a configspec.
2121
It uses the ``validator`` object from *validate.py*.
2123
To run ``validate`` on the current ConfigObj, call: ::
2125
test = config.validate(validator)
2127
(Normally having previously passed in the configspec when the ConfigObj
2128
was created - you can dynamically assign a dictionary of checks to the
2129
``configspec`` attribute of a section though).
2131
It returns ``True`` if everything passes, or a dictionary of
2132
pass/fails (True/False). If every member of a subsection passes, it
2133
will just have the value ``True``. (It also returns ``False`` if all
2136
In addition, it converts the values from strings to their native
2137
types if their checks pass (and ``stringify`` is set).
2139
If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2140
of a marking a fail with a ``False``, it will preserve the actual
2141
exception object. This can contain info about the reason for failure.
2142
For example the ``VdtValueTooSmallError`` indicates that the value
2143
supplied was too small. If a value (or section) is missing it will
2144
still be marked as ``False``.
2146
You must have the validate module to use ``preserve_errors=True``.
2148
You can then use the ``flatten_errors`` function to turn your nested
2149
results dictionary into a flattened list of failures - useful for
2150
displaying meaningful error messages.
2153
if self.configspec is None:
2154
raise ValueError('No configspec supplied.')
2156
# We do this once to remove a top level dependency on the validate module
2157
# Which makes importing configobj faster
2158
from validate import VdtMissingValue
2159
self._vdtMissingValue = VdtMissingValue
2164
section.initial_comment = section.configspec.initial_comment
2165
section.final_comment = section.configspec.final_comment
2166
section.encoding = section.configspec.encoding
2167
section.BOM = section.configspec.BOM
2168
section.newlines = section.configspec.newlines
2169
section.indent_type = section.configspec.indent_type
2172
# section.default_values.clear() #??
2173
configspec = section.configspec
2174
self._set_configspec(section, copy)
2177
def validate_entry(entry, spec, val, missing, ret_true, ret_false):
2178
section.default_values.pop(entry, None)
2181
section.default_values[entry] = validator.get_default_value(configspec[entry])
2182
except (KeyError, AttributeError, validator.baseErrorClass):
2183
# No default, bad default or validator has no 'get_default_value'
2188
check = validator.check(spec,
2192
except validator.baseErrorClass, e:
2193
if not preserve_errors or isinstance(e, self._vdtMissingValue):
2196
# preserve the error
2203
if self.stringify or missing:
2204
# if we are doing type conversion
2205
# or the value is a supplied default
2206
if not self.stringify:
2207
if isinstance(check, (list, tuple)):
2209
check = [self._str(item) for item in check]
2210
elif missing and check is None:
2211
# convert the None from a default to a ''
2214
check = self._str(check)
2215
if (check != val) or missing:
2216
section[entry] = check
2217
if not copy and missing and entry not in section.defaults:
2218
section.defaults.append(entry)
2219
return ret_true, ret_false
2226
unvalidated = [k for k in section.scalars if k not in configspec]
2227
incorrect_sections = [k for k in configspec.sections if k in section.scalars]
2228
incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2230
for entry in configspec.scalars:
2231
if entry in ('__many__', '___many___'):
2234
if (not entry in section.scalars) or (entry in section.defaults):
2236
# or entries from defaults
2239
if copy and entry not in section.scalars:
2241
section.comments[entry] = (
2242
configspec.comments.get(entry, []))
2243
section.inline_comments[entry] = (
2244
configspec.inline_comments.get(entry, ''))
2248
val = section[entry]
2250
ret_true, ret_false = validate_entry(entry, configspec[entry], val,
2251
missing, ret_true, ret_false)
2254
if '__many__' in configspec.scalars:
2255
many = configspec['__many__']
2256
elif '___many___' in configspec.scalars:
2257
many = configspec['___many___']
2259
if many is not None:
2260
for entry in unvalidated:
2261
val = section[entry]
2262
ret_true, ret_false = validate_entry(entry, many, val, False,
2263
ret_true, ret_false)
2266
for entry in incorrect_scalars:
2268
if not preserve_errors:
2272
msg = 'Value %r was provided as a section' % entry
2273
out[entry] = validator.baseErrorClass(msg)
2274
for entry in incorrect_sections:
2276
if not preserve_errors:
2280
msg = 'Section %r was provided as a single value' % entry
2281
out[entry] = validator.baseErrorClass(msg)
2283
# Missing sections will have been created as empty ones when the
2284
# configspec was read.
2285
for entry in section.sections:
2286
# FIXME: this means DEFAULT is not copied in copy mode
2287
if section is self and entry == 'DEFAULT':
2289
if section[entry].configspec is None:
2290
unvalidated.append(entry)
2293
section.comments[entry] = configspec.comments.get(entry, [])
2294
section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
2295
check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
2304
section.extra_values = unvalidated
2305
if preserve_errors and not section._created:
2306
# If the section wasn't created (i.e. it wasn't missing)
2307
# then we can't return False, we need to preserve errors
2310
if ret_false and preserve_errors and out:
2311
# If we are preserving errors, but all
2312
# the failures are from missing sections / values
2313
# then we can return False. Otherwise there is a
2314
# real failure that we need to preserve.
2315
ret_false = not any(out.values())
2324
"""Clear ConfigObj instance and restore to 'freshly created' state."""
2327
# FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2328
# requires an empty dictionary
2329
self.configspec = None
2330
# Just to be sure ;-)
2331
self._original_configspec = None
2336
Reload a ConfigObj from file.
2338
This method raises a ``ReloadError`` if the ConfigObj doesn't have
2339
a filename attribute pointing to a file.
2341
if not isinstance(self.filename, basestring):
2344
filename = self.filename
2345
current_options = {}
2346
for entry in OPTION_DEFAULTS:
2347
if entry == 'configspec':
2349
current_options[entry] = getattr(self, entry)
2351
configspec = self._original_configspec
2352
current_options['configspec'] = configspec
2355
self._initialise(current_options)
2356
self._load(filename, configspec)
2360
class SimpleVal(object):
2363
Can be used to check that all members expected are present.
2365
To use it, provide a configspec with all your members in (the value given
2366
will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2367
method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2368
members are present, or a dictionary with True/False meaning
2369
present/missing. (Whole missing sections will be replaced with ``False``)
2373
self.baseErrorClass = ConfigObjError
2375
def check(self, check, member, missing=False):
2376
"""A dummy check method, always returns the value unchanged."""
2378
raise self.baseErrorClass()
2382
def flatten_errors(cfg, res, levels=None, results=None):
2384
An example function that will turn a nested dictionary of results
2385
(as returned by ``ConfigObj.validate``) into a flat list.
2387
``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2388
dictionary returned by ``validate``.
2390
(This is a recursive function, so you shouldn't use the ``levels`` or
2391
``results`` arguments - they are used by the function.)
2393
Returns a list of keys that failed. Each member of the list is a tuple::
2395
([list of sections...], key, result)
2397
If ``validate`` was called with ``preserve_errors=False`` (the default)
2398
then ``result`` will always be ``False``.
2400
*list of sections* is a flattened list of sections that the key was found
2403
If the section was missing (or a section was expected and a scalar provided
2404
- or vice-versa) then key will be ``None``.
2406
If the value (or section) was missing then ``result`` will be ``False``.
2408
If ``validate`` was called with ``preserve_errors=True`` and a value
2409
was present, but failed the check, then ``result`` will be the exception
2410
object returned. You can use this as a string that describes the failure.
2412
For example *The value "3" is of the wrong type*.
2420
if res == False or isinstance(res, Exception):
2421
results.append((levels[:], None, res))
2425
for (key, val) in res.items():
2428
if isinstance(cfg.get(key), dict):
2431
flatten_errors(cfg[key], val, levels, results)
2433
results.append((levels[:], key, val))
2442
def get_extra_values(conf, _prepend=()):
2444
Find all the values and sections not in the configspec from a validated
2447
``get_extra_values`` returns a list of tuples where each tuple represents
2448
either an extra section, or an extra value.
2450
The tuples contain two values, a tuple representing the section the value
2451
is in and the name of the extra values. For extra values in the top level
2452
section the first member will be an empty tuple. For values in the 'foo'
2453
section the first member will be ``('foo',)``. For members in the 'bar'
2454
subsection of the 'foo' section the first member will be ``('foo', 'bar')``.
2456
NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't
2457
been validated it will return an empty list.
2461
out.extend([(_prepend, name) for name in conf.extra_values])
2462
for name in conf.sections:
2463
if name not in conf.extra_values:
2464
out.extend(get_extra_values(conf[name], _prepend + (name,)))
2468
"""*A programming language is a medium of expression.* - Paul Graham"""