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
1014
return float(self[key])
1017
def as_list(self, key):
1019
A convenience method which fetches the specified value, guaranteeing
1034
if isinstance(result, (tuple, list)):
1039
def restore_default(self, key):
1041
Restore (and return) default value for the specified key.
1043
This method will only work for a ConfigObj that was created
1044
with a configspec and has been validated.
1046
If there is no default value for this key, ``KeyError`` is raised.
1048
default = self.default_values[key]
1049
dict.__setitem__(self, key, default)
1050
if key not in self.defaults:
1051
self.defaults.append(key)
1055
def restore_defaults(self):
1057
Recursively restore default values to all members
1060
This method will only work for a ConfigObj that was created
1061
with a configspec and has been validated.
1063
It doesn't delete or modify entries without default values.
1065
for key in self.default_values:
1066
self.restore_default(key)
1068
for section in self.sections:
1069
self[section].restore_defaults()
1072
class ConfigObj(Section):
1073
"""An object to read, create, and write config files."""
1075
_keyword = re.compile(r'''^ # line start
1078
(?:".*?")| # double quotes
1079
(?:'.*?')| # single quotes
1080
(?:[^'"=].*?) # no quotes
1083
(.*) # value (including list values and comments)
1088
_sectionmarker = re.compile(r'''^
1089
(\s*) # 1: indentation
1090
((?:\[\s*)+) # 2: section marker open
1091
( # 3: section name open
1092
(?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1093
(?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1094
(?:[^'"\s].*?) # at least one non-space unquoted
1095
) # section name close
1096
((?:\s*\])+) # 4: section marker close
1097
\s*(\#.*)? # 5: optional comment
1101
# this regexp pulls list values out as a single string
1102
# or single values and comments
1103
# FIXME: this regex adds a '' to the end of comma terminated lists
1104
# workaround in ``_handle_value``
1105
_valueexp = re.compile(r'''^
1111
(?:".*?")| # double quotes
1112
(?:'.*?')| # single quotes
1113
(?:[^'",\#][^,\#]*?) # unquoted
1116
)* # match all list items ending in a comma (if any)
1119
(?:".*?")| # double quotes
1120
(?:'.*?')| # single quotes
1121
(?:[^'",\#\s][^,]*?)| # unquoted
1122
(?:(?<!,)) # Empty value
1123
)? # last item in a list - or string value
1125
(,) # alternatively a single comma - empty list
1127
\s*(\#.*)? # optional comment
1131
# use findall to get the members of a list value
1132
_listvalueexp = re.compile(r'''
1134
(?:".*?")| # double quotes
1135
(?:'.*?')| # single quotes
1136
(?:[^'",\#]?.*?) # unquoted
1142
# this regexp is used for the value
1143
# when lists are switched off
1144
_nolistvalue = re.compile(r'''^
1146
(?:".*?")| # double quotes
1147
(?:'.*?')| # single quotes
1148
(?:[^'"\#].*?)| # unquoted
1151
\s*(\#.*)? # optional comment
1155
# regexes for finding triple quoted values on one line
1156
_single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1157
_single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1158
_multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1159
_multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1162
"'''": (_single_line_single, _multi_line_single),
1163
'"""': (_single_line_double, _multi_line_double),
1166
# Used by the ``istrue`` Section method
1168
'yes': True, 'no': False,
1169
'on': True, 'off': False,
1170
'1': True, '0': False,
1171
'true': True, 'false': False,
1175
def __init__(self, infile=None, options=None, configspec=None, encoding=None,
1176
interpolation=True, raise_errors=False, list_values=True,
1177
create_empty=False, file_error=False, stringify=True,
1178
indent_type=None, default_encoding=None, unrepr=False,
1179
write_empty_values=False, _inspec=False):
1181
Parse a config file or create a config file object.
1183
``ConfigObj(infile=None, configspec=None, encoding=None,
1184
interpolation=True, raise_errors=False, list_values=True,
1185
create_empty=False, file_error=False, stringify=True,
1186
indent_type=None, default_encoding=None, unrepr=False,
1187
write_empty_values=False, _inspec=False)``
1189
self._inspec = _inspec
1190
# init the superclass
1191
Section.__init__(self, self, 0, self)
1193
infile = infile or []
1195
_options = {'configspec': configspec,
1196
'encoding': encoding, 'interpolation': interpolation,
1197
'raise_errors': raise_errors, 'list_values': list_values,
1198
'create_empty': create_empty, 'file_error': file_error,
1199
'stringify': stringify, 'indent_type': indent_type,
1200
'default_encoding': default_encoding, 'unrepr': unrepr,
1201
'write_empty_values': write_empty_values}
1207
warnings.warn('Passing in an options dictionary to ConfigObj() is '
1208
'deprecated. Use **options instead.',
1209
DeprecationWarning, stacklevel=2)
1211
# TODO: check the values too.
1212
for entry in options:
1213
if entry not in OPTION_DEFAULTS:
1214
raise TypeError('Unrecognised option "%s".' % entry)
1215
for entry, value in OPTION_DEFAULTS.items():
1216
if entry not in options:
1217
options[entry] = value
1218
keyword_value = _options[entry]
1219
if value != keyword_value:
1220
options[entry] = keyword_value
1222
# XXXX this ignores an explicit list_values = True in combination
1223
# with _inspec. The user should *never* do that anyway, but still...
1225
options['list_values'] = False
1227
self._initialise(options)
1228
configspec = options['configspec']
1229
self._original_configspec = configspec
1230
self._load(infile, configspec)
1233
def _load(self, infile, configspec):
1234
if isinstance(infile, basestring):
1235
self.filename = infile
1236
if os.path.isfile(infile):
1237
h = open(infile, 'rb')
1238
infile = h.read() or []
1240
elif self.file_error:
1241
# raise an error if the file doesn't exist
1242
raise IOError('Config file not found: "%s".' % self.filename)
1244
# file doesn't already exist
1245
if self.create_empty:
1246
# this is a good test that the filename specified
1247
# isn't impossible - like on a non-existent device
1248
h = open(infile, 'w')
1253
elif isinstance(infile, (list, tuple)):
1254
infile = list(infile)
1256
elif isinstance(infile, dict):
1258
# the Section class handles creating subsections
1259
if isinstance(infile, ConfigObj):
1260
# get a copy of our ConfigObj
1261
def set_section(in_section, this_section):
1262
for entry in in_section.scalars:
1263
this_section[entry] = in_section[entry]
1264
for section in in_section.sections:
1265
this_section[section] = {}
1266
set_section(in_section[section], this_section[section])
1267
set_section(infile, self)
1270
for entry in infile:
1271
self[entry] = infile[entry]
1274
if configspec is not None:
1275
self._handle_configspec(configspec)
1277
self.configspec = None
1280
elif getattr(infile, 'read', MISSING) is not MISSING:
1281
# This supports file like objects
1282
infile = infile.read() or []
1283
# needs splitting into lines - but needs doing *after* decoding
1284
# in case it's not an 8 bit encoding
1286
raise TypeError('infile must be a filename, file like object, or list of lines.')
1289
# don't do it for the empty ConfigObj
1290
infile = self._handle_bom(infile)
1291
# infile is now *always* a list
1293
# Set the newlines attribute (first line ending it finds)
1294
# and strip trailing '\n' or '\r' from lines
1296
if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1298
for end in ('\r\n', '\n', '\r'):
1299
if line.endswith(end):
1304
infile = [line.rstrip('\r\n') for line in infile]
1307
# if we had any errors, now is the time to raise them
1309
info = "at line %s." % self._errors[0].line_number
1310
if len(self._errors) > 1:
1311
msg = "Parsing failed with several errors.\nFirst error %s" % info
1312
error = ConfigObjError(msg)
1314
error = self._errors[0]
1315
# set the errors attribute; it's a list of tuples:
1316
# (error_type, message, line_number)
1317
error.errors = self._errors
1318
# set the config attribute
1321
# delete private attributes
1324
if configspec is None:
1325
self.configspec = None
1327
self._handle_configspec(configspec)
1330
def _initialise(self, options=None):
1332
options = OPTION_DEFAULTS
1334
# initialise a few variables
1335
self.filename = None
1337
self.raise_errors = options['raise_errors']
1338
self.interpolation = options['interpolation']
1339
self.list_values = options['list_values']
1340
self.create_empty = options['create_empty']
1341
self.file_error = options['file_error']
1342
self.stringify = options['stringify']
1343
self.indent_type = options['indent_type']
1344
self.encoding = options['encoding']
1345
self.default_encoding = options['default_encoding']
1347
self.newlines = None
1348
self.write_empty_values = options['write_empty_values']
1349
self.unrepr = options['unrepr']
1351
self.initial_comment = []
1352
self.final_comment = []
1353
self.configspec = None
1356
self.list_values = False
1358
# Clear section attributes as well
1359
Section._initialise(self)
1366
except MissingInterpolationOption:
1367
return dict.__getitem__(self, key)
1368
return ('ConfigObj({%s})' %
1369
', '.join([('%s: %s' % (repr(key), repr(_getval(key))))
1370
for key in (self.scalars + self.sections)]))
1373
def _handle_bom(self, infile):
1375
Handle any BOM, and decode if necessary.
1377
If an encoding is specified, that *must* be used - but the BOM should
1378
still be removed (and the BOM attribute set).
1380
(If the encoding is wrongly specified, then a BOM for an alternative
1381
encoding won't be discovered or removed.)
1383
If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1384
removed. The BOM attribute will be set. UTF16 will be decoded to
1387
NOTE: This method must not be called with an empty ``infile``.
1389
Specifying the *wrong* encoding is likely to cause a
1390
``UnicodeDecodeError``.
1392
``infile`` must always be returned as a list of lines, but may be
1393
passed in as a single string.
1395
if ((self.encoding is not None) and
1396
(self.encoding.lower() not in BOM_LIST)):
1397
# No need to check for a BOM
1398
# the encoding specified doesn't have one
1400
return self._decode(infile, self.encoding)
1402
if isinstance(infile, (list, tuple)):
1406
if self.encoding is not None:
1407
# encoding explicitly supplied
1408
# And it could have an associated BOM
1409
# TODO: if encoding is just UTF16 - we ought to check for both
1410
# TODO: big endian and little endian versions.
1411
enc = BOM_LIST[self.encoding.lower()]
1413
# For UTF16 we try big endian and little endian
1414
for BOM, (encoding, final_encoding) in BOMS.items():
1415
if not final_encoding:
1418
if infile.startswith(BOM):
1421
# Don't need to remove BOM
1422
return self._decode(infile, encoding)
1424
# If we get this far, will *probably* raise a DecodeError
1425
# As it doesn't appear to start with a BOM
1426
return self._decode(infile, self.encoding)
1430
if not line.startswith(BOM):
1431
return self._decode(infile, self.encoding)
1433
newline = line[len(BOM):]
1436
if isinstance(infile, (list, tuple)):
1441
return self._decode(infile, self.encoding)
1443
# No encoding specified - so we need to check for UTF8/UTF16
1444
for BOM, (encoding, final_encoding) in BOMS.items():
1445
if not line.startswith(BOM):
1449
self.encoding = final_encoding
1450
if not final_encoding:
1454
newline = line[len(BOM):]
1455
if isinstance(infile, (list, tuple)):
1459
# UTF8 - don't decode
1460
if isinstance(infile, basestring):
1461
return infile.splitlines(True)
1464
# UTF16 - have to decode
1465
return self._decode(infile, encoding)
1467
# No BOM discovered and no encoding specified, just return
1468
if isinstance(infile, basestring):
1469
# infile read from a file will be a single string
1470
return infile.splitlines(True)
1474
def _a_to_u(self, aString):
1475
"""Decode ASCII strings to unicode if a self.encoding is specified."""
1477
return aString.decode('ascii')
1482
def _decode(self, infile, encoding):
1484
Decode infile to unicode. Using the specified encoding.
1486
if is a string, it also needs converting to a list.
1488
if isinstance(infile, basestring):
1490
# NOTE: Could raise a ``UnicodeDecodeError``
1491
return infile.decode(encoding).splitlines(True)
1492
for i, line in enumerate(infile):
1493
if not isinstance(line, unicode):
1494
# NOTE: The isinstance test here handles mixed lists of unicode/string
1495
# NOTE: But the decode will break on any non-string values
1496
# NOTE: Or could raise a ``UnicodeDecodeError``
1497
infile[i] = line.decode(encoding)
1501
def _decode_element(self, line):
1502
"""Decode element to unicode if necessary."""
1503
if not self.encoding:
1505
if isinstance(line, str) and self.default_encoding:
1506
return line.decode(self.default_encoding)
1510
def _str(self, value):
1512
Used by ``stringify`` within validate, to turn non-string values
1515
if not isinstance(value, basestring):
1521
def _parse(self, infile):
1522
"""Actually parse the config file."""
1523
temp_list_values = self.list_values
1525
self.list_values = False
1530
maxline = len(infile) - 1
1532
reset_comment = False
1534
while cur_index < maxline:
1538
line = infile[cur_index]
1539
sline = line.strip()
1540
# do we have anything on the line ?
1541
if not sline or sline.startswith('#'):
1542
reset_comment = False
1543
comment_list.append(line)
1547
# preserve initial comment
1548
self.initial_comment = comment_list
1552
reset_comment = True
1553
# first we check if it's a section marker
1554
mat = self._sectionmarker.match(line)
1557
(indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1558
if indent and (self.indent_type is None):
1559
self.indent_type = indent
1560
cur_depth = sect_open.count('[')
1561
if cur_depth != sect_close.count(']'):
1562
self._handle_error("Cannot compute the section depth at line %s.",
1563
NestingError, infile, cur_index)
1566
if cur_depth < this_section.depth:
1567
# the new section is dropping back to a previous level
1569
parent = self._match_depth(this_section,
1572
self._handle_error("Cannot compute nesting level at line %s.",
1573
NestingError, infile, cur_index)
1575
elif cur_depth == this_section.depth:
1576
# the new section is a sibling of the current section
1577
parent = this_section.parent
1578
elif cur_depth == this_section.depth + 1:
1579
# the new section is a child the current section
1580
parent = this_section
1582
self._handle_error("Section too nested at line %s.",
1583
NestingError, infile, cur_index)
1585
sect_name = self._unquote(sect_name)
1586
if sect_name in parent:
1587
self._handle_error('Duplicate section name at line %s.',
1588
DuplicateError, infile, cur_index)
1591
# create the new section
1592
this_section = Section(
1597
parent[sect_name] = this_section
1598
parent.inline_comments[sect_name] = comment
1599
parent.comments[sect_name] = comment_list
1602
# it's not a section marker,
1603
# so it should be a valid ``key = value`` line
1604
mat = self._keyword.match(line)
1606
# it neither matched as a keyword
1607
# or a section marker
1609
'Invalid line at line "%s".',
1610
ParseError, infile, cur_index)
1612
# is a keyword value
1613
# value will include any inline comment
1614
(indent, key, value) = mat.groups()
1615
if indent and (self.indent_type is None):
1616
self.indent_type = indent
1617
# check for a multiline value
1618
if value[:3] in ['"""', "'''"]:
1620
value, comment, cur_index = self._multiline(
1621
value, infile, cur_index, maxline)
1624
'Parse error in value at line %s.',
1625
ParseError, infile, cur_index)
1631
value = unrepr(value)
1632
except Exception, e:
1633
if type(e) == UnknownType:
1634
msg = 'Unknown name or type in value at line %s.'
1636
msg = 'Parse error in value at line %s.'
1637
self._handle_error(msg, UnreprError, infile,
1644
value = unrepr(value)
1645
except Exception, e:
1646
if isinstance(e, UnknownType):
1647
msg = 'Unknown name or type in value at line %s.'
1649
msg = 'Parse error in value at line %s.'
1650
self._handle_error(msg, UnreprError, infile,
1654
# extract comment and lists
1656
(value, comment) = self._handle_value(value)
1659
'Parse error in value at line %s.',
1660
ParseError, infile, cur_index)
1663
key = self._unquote(key)
1664
if key in this_section:
1666
'Duplicate keyword name at line %s.',
1667
DuplicateError, infile, cur_index)
1670
# we set unrepr because if we have got this far we will never
1671
# be creating a new section
1672
this_section.__setitem__(key, value, unrepr=True)
1673
this_section.inline_comments[key] = comment
1674
this_section.comments[key] = comment_list
1677
if self.indent_type is None:
1678
# no indentation used, set the type accordingly
1679
self.indent_type = ''
1681
# preserve the final comment
1682
if not self and not self.initial_comment:
1683
self.initial_comment = comment_list
1684
elif not reset_comment:
1685
self.final_comment = comment_list
1686
self.list_values = temp_list_values
1689
def _match_depth(self, sect, depth):
1691
Given a section and a depth level, walk back through the sections
1692
parents to see if the depth level matches a previous section.
1694
Return a reference to the right section,
1695
or raise a SyntaxError.
1697
while depth < sect.depth:
1698
if sect is sect.parent:
1699
# we've reached the top level already
1702
if sect.depth == depth:
1704
# shouldn't get here
1708
def _handle_error(self, text, ErrorClass, infile, cur_index):
1710
Handle an error according to the error settings.
1712
Either raise the error or store it.
1713
The error will have occured at ``cur_index``
1715
line = infile[cur_index]
1717
message = text % cur_index
1718
error = ErrorClass(message, cur_index, line)
1719
if self.raise_errors:
1720
# raise the error - parsing stops here
1723
# reraise when parsing has finished
1724
self._errors.append(error)
1727
def _unquote(self, value):
1728
"""Return an unquoted version of a value"""
1730
# should only happen during parsing of lists
1732
if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1737
def _quote(self, value, multiline=True):
1739
Return a safely quoted version of a value.
1741
Raise a ConfigObjError if the value cannot be safely quoted.
1742
If multiline is ``True`` (default) then use triple quotes
1745
* Don't quote values that don't need it.
1746
* Recursively quote members of a list and return a comma joined list.
1747
* Multiline is ``False`` for lists.
1748
* Obey list syntax for empty and single member lists.
1750
If ``list_values=False`` then the value is only quoted if it contains
1751
a ``\\n`` (is multiline) or '#'.
1753
If ``write_empty_values`` is set, and the value is an empty string, it
1756
if multiline and self.write_empty_values and value == '':
1757
# Only if multiline is set, so that it is used for values not
1758
# keys, and not values that are part of a list
1761
if multiline and isinstance(value, (list, tuple)):
1764
elif len(value) == 1:
1765
return self._quote(value[0], multiline=False) + ','
1766
return ', '.join([self._quote(val, multiline=False)
1768
if not isinstance(value, basestring):
1772
raise TypeError('Value "%s" is not a string.' % value)
1777
no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1778
need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1779
hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1780
check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1782
if check_for_single:
1783
if not self.list_values:
1784
# we don't quote if ``list_values=False``
1786
# for normal values either single or double quotes will do
1788
# will only happen if multiline is off - e.g. '\n' in key
1789
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1790
elif ((value[0] not in wspace_plus) and
1791
(value[-1] not in wspace_plus) and
1792
(',' not in value)):
1795
quot = self._get_single_quote(value)
1797
# if value has '\n' or "'" *and* '"', it will need triple quotes
1798
quot = self._get_triple_quote(value)
1800
if quot == noquot and '#' in value and self.list_values:
1801
quot = self._get_single_quote(value)
1806
def _get_single_quote(self, value):
1807
if ("'" in value) and ('"' in value):
1808
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1816
def _get_triple_quote(self, value):
1817
if (value.find('"""') != -1) and (value.find("'''") != -1):
1818
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1819
if value.find('"""') == -1:
1826
def _handle_value(self, value):
1828
Given a value string, unquote, remove comment,
1829
handle lists. (including empty and single member lists)
1832
# Parsing a configspec so don't handle comments
1834
# do we look for lists in values ?
1835
if not self.list_values:
1836
mat = self._nolistvalue.match(value)
1839
# NOTE: we don't unquote here
1842
mat = self._valueexp.match(value)
1844
# the value is badly constructed, probably badly quoted,
1845
# or an invalid list
1847
(list_values, single, empty_list, comment) = mat.groups()
1848
if (list_values == '') and (single is None):
1849
# change this if you want to accept empty values
1851
# NOTE: note there is no error handling from here if the regex
1852
# is wrong: then incorrect values will slip through
1853
if empty_list is not None:
1854
# the single comma - meaning an empty list
1855
return ([], comment)
1856
if single is not None:
1857
# handle empty values
1858
if list_values and not single:
1859
# FIXME: the '' is a workaround because our regex now matches
1860
# '' at the end of a list if it has a trailing comma
1863
single = single or '""'
1864
single = self._unquote(single)
1865
if list_values == '':
1867
return (single, comment)
1868
the_list = self._listvalueexp.findall(list_values)
1869
the_list = [self._unquote(val) for val in the_list]
1870
if single is not None:
1871
the_list += [single]
1872
return (the_list, comment)
1875
def _multiline(self, value, infile, cur_index, maxline):
1876
"""Extract the value, where we are in a multiline situation."""
1878
newvalue = value[3:]
1879
single_line = self._triple_quote[quot][0]
1880
multi_line = self._triple_quote[quot][1]
1881
mat = single_line.match(value)
1883
retval = list(mat.groups())
1884
retval.append(cur_index)
1886
elif newvalue.find(quot) != -1:
1887
# somehow the triple quote is missing
1890
while cur_index < maxline:
1893
line = infile[cur_index]
1894
if line.find(quot) == -1:
1897
# end of multiline, process it
1900
# we've got to the end of the config, oops...
1902
mat = multi_line.match(line)
1904
# a badly formed line
1906
(value, comment) = mat.groups()
1907
return (newvalue + value, comment, cur_index)
1910
def _handle_configspec(self, configspec):
1911
"""Parse the configspec."""
1912
# FIXME: Should we check that the configspec was created with the
1913
# correct settings ? (i.e. ``list_values=False``)
1914
if not isinstance(configspec, ConfigObj):
1916
configspec = ConfigObj(configspec,
1920
except ConfigObjError, e:
1921
# FIXME: Should these errors have a reference
1922
# to the already parsed ConfigObj ?
1923
raise ConfigspecError('Parsing configspec failed: %s' % e)
1925
raise IOError('Reading configspec failed: %s' % e)
1927
self.configspec = configspec
1931
def _set_configspec(self, section, copy):
1933
Called by validate. Handles setting the configspec on subsections
1934
including sections to be validated by __many__
1936
configspec = section.configspec
1937
many = configspec.get('__many__')
1938
if isinstance(many, dict):
1939
for entry in section.sections:
1940
if entry not in configspec:
1941
section[entry].configspec = many
1943
for entry in configspec.sections:
1944
if entry == '__many__':
1946
if entry not in section:
1948
section[entry]._created = True
1951
section.comments[entry] = configspec.comments.get(entry, [])
1952
section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1954
# Could be a scalar when we expect a section
1955
if isinstance(section[entry], Section):
1956
section[entry].configspec = configspec[entry]
1959
def _write_line(self, indent_string, entry, this_entry, comment):
1960
"""Write an individual line, for the write method"""
1961
# NOTE: the calls to self._quote here handles non-StringType values.
1963
val = self._decode_element(self._quote(this_entry))
1965
val = repr(this_entry)
1966
return '%s%s%s%s%s' % (indent_string,
1967
self._decode_element(self._quote(entry, multiline=False)),
1968
self._a_to_u(' = '),
1970
self._decode_element(comment))
1973
def _write_marker(self, indent_string, depth, entry, comment):
1974
"""Write a section marker line"""
1975
return '%s%s%s%s%s' % (indent_string,
1976
self._a_to_u('[' * depth),
1977
self._quote(self._decode_element(entry), multiline=False),
1978
self._a_to_u(']' * depth),
1979
self._decode_element(comment))
1982
def _handle_comment(self, comment):
1983
"""Deal with a comment."""
1986
start = self.indent_type
1987
if not comment.startswith('#'):
1988
start += self._a_to_u(' # ')
1989
return (start + comment)
1994
def write(self, outfile=None, section=None):
1996
Write the current ConfigObj as a file
1998
tekNico: FIXME: use StringIO instead of real files
2000
>>> filename = a.filename
2001
>>> a.filename = 'test.ini'
2003
>>> a.filename = filename
2004
>>> a == ConfigObj('test.ini', raise_errors=True)
2007
>>> os.remove('test.ini')
2009
if self.indent_type is None:
2010
# this can be true if initialised from a dictionary
2011
self.indent_type = DEFAULT_INDENT_TYPE
2014
cs = self._a_to_u('#')
2015
csp = self._a_to_u('# ')
2017
int_val = self.interpolation
2018
self.interpolation = False
2020
for line in self.initial_comment:
2021
line = self._decode_element(line)
2022
stripped_line = line.strip()
2023
if stripped_line and not stripped_line.startswith(cs):
2027
indent_string = self.indent_type * section.depth
2028
for entry in (section.scalars + section.sections):
2029
if entry in section.defaults:
2030
# don't write out default values
2032
for comment_line in section.comments[entry]:
2033
comment_line = self._decode_element(comment_line.lstrip())
2034
if comment_line and not comment_line.startswith(cs):
2035
comment_line = csp + comment_line
2036
out.append(indent_string + comment_line)
2037
this_entry = section[entry]
2038
comment = self._handle_comment(section.inline_comments[entry])
2040
if isinstance(this_entry, dict):
2042
out.append(self._write_marker(
2047
out.extend(self.write(section=this_entry))
2049
out.append(self._write_line(
2056
for line in self.final_comment:
2057
line = self._decode_element(line)
2058
stripped_line = line.strip()
2059
if stripped_line and not stripped_line.startswith(cs):
2062
self.interpolation = int_val
2064
if section is not self:
2067
if (self.filename is None) and (outfile is None):
2068
# output a list of lines
2069
# might need to encode
2070
# NOTE: This will *screw* UTF16, each line will start with the BOM
2072
out = [l.encode(self.encoding) for l in out]
2073
if (self.BOM and ((self.encoding is None) or
2074
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2078
out[0] = BOM_UTF8 + out[0]
2081
# Turn the list to a string, joined with correct newlines
2082
newline = self.newlines or os.linesep
2083
if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w'
2084
and sys.platform == 'win32' and newline == '\r\n'):
2085
# Windows specific hack to avoid writing '\r\r\n'
2087
output = self._a_to_u(newline).join(out)
2089
output = output.encode(self.encoding)
2090
if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2092
output = BOM_UTF8 + output
2094
if not output.endswith(newline):
2096
if outfile is not None:
2097
outfile.write(output)
2099
h = open(self.filename, 'wb')
2104
def validate(self, validator, preserve_errors=False, copy=False,
2107
Test the ConfigObj against a configspec.
2109
It uses the ``validator`` object from *validate.py*.
2111
To run ``validate`` on the current ConfigObj, call: ::
2113
test = config.validate(validator)
2115
(Normally having previously passed in the configspec when the ConfigObj
2116
was created - you can dynamically assign a dictionary of checks to the
2117
``configspec`` attribute of a section though).
2119
It returns ``True`` if everything passes, or a dictionary of
2120
pass/fails (True/False). If every member of a subsection passes, it
2121
will just have the value ``True``. (It also returns ``False`` if all
2124
In addition, it converts the values from strings to their native
2125
types if their checks pass (and ``stringify`` is set).
2127
If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2128
of a marking a fail with a ``False``, it will preserve the actual
2129
exception object. This can contain info about the reason for failure.
2130
For example the ``VdtValueTooSmallError`` indicates that the value
2131
supplied was too small. If a value (or section) is missing it will
2132
still be marked as ``False``.
2134
You must have the validate module to use ``preserve_errors=True``.
2136
You can then use the ``flatten_errors`` function to turn your nested
2137
results dictionary into a flattened list of failures - useful for
2138
displaying meaningful error messages.
2141
if self.configspec is None:
2142
raise ValueError('No configspec supplied.')
2144
# We do this once to remove a top level dependency on the validate module
2145
# Which makes importing configobj faster
2146
from validate import VdtMissingValue
2147
self._vdtMissingValue = VdtMissingValue
2152
section.initial_comment = section.configspec.initial_comment
2153
section.final_comment = section.configspec.final_comment
2154
section.encoding = section.configspec.encoding
2155
section.BOM = section.configspec.BOM
2156
section.newlines = section.configspec.newlines
2157
section.indent_type = section.configspec.indent_type
2160
# section.default_values.clear() #??
2161
configspec = section.configspec
2162
self._set_configspec(section, copy)
2165
def validate_entry(entry, spec, val, missing, ret_true, ret_false):
2166
section.default_values.pop(entry, None)
2169
section.default_values[entry] = validator.get_default_value(configspec[entry])
2170
except (KeyError, AttributeError, validator.baseErrorClass):
2171
# No default, bad default or validator has no 'get_default_value'
2176
check = validator.check(spec,
2180
except validator.baseErrorClass, e:
2181
if not preserve_errors or isinstance(e, self._vdtMissingValue):
2184
# preserve the error
2191
if self.stringify or missing:
2192
# if we are doing type conversion
2193
# or the value is a supplied default
2194
if not self.stringify:
2195
if isinstance(check, (list, tuple)):
2197
check = [self._str(item) for item in check]
2198
elif missing and check is None:
2199
# convert the None from a default to a ''
2202
check = self._str(check)
2203
if (check != val) or missing:
2204
section[entry] = check
2205
if not copy and missing and entry not in section.defaults:
2206
section.defaults.append(entry)
2207
return ret_true, ret_false
2214
unvalidated = [k for k in section.scalars if k not in configspec]
2215
incorrect_sections = [k for k in configspec.sections if k in section.scalars]
2216
incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2218
for entry in configspec.scalars:
2219
if entry in ('__many__', '___many___'):
2222
if (not entry in section.scalars) or (entry in section.defaults):
2224
# or entries from defaults
2227
if copy and entry not in section.scalars:
2229
section.comments[entry] = (
2230
configspec.comments.get(entry, []))
2231
section.inline_comments[entry] = (
2232
configspec.inline_comments.get(entry, ''))
2236
val = section[entry]
2238
ret_true, ret_false = validate_entry(entry, configspec[entry], val,
2239
missing, ret_true, ret_false)
2242
if '__many__' in configspec.scalars:
2243
many = configspec['__many__']
2244
elif '___many___' in configspec.scalars:
2245
many = configspec['___many___']
2247
if many is not None:
2248
for entry in unvalidated:
2249
val = section[entry]
2250
ret_true, ret_false = validate_entry(entry, many, val, False,
2251
ret_true, ret_false)
2254
for entry in incorrect_scalars:
2256
if not preserve_errors:
2260
msg = 'Value %r was provided as a section' % entry
2261
out[entry] = validator.baseErrorClass(msg)
2262
for entry in incorrect_sections:
2264
if not preserve_errors:
2268
msg = 'Section %r was provided as a single value' % entry
2269
out[entry] = validator.baseErrorClass(msg)
2271
# Missing sections will have been created as empty ones when the
2272
# configspec was read.
2273
for entry in section.sections:
2274
# FIXME: this means DEFAULT is not copied in copy mode
2275
if section is self and entry == 'DEFAULT':
2277
if section[entry].configspec is None:
2278
unvalidated.append(entry)
2281
section.comments[entry] = configspec.comments.get(entry, [])
2282
section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
2283
check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
2292
section.extra_values = unvalidated
2293
if preserve_errors and not section._created:
2294
# If the section wasn't created (i.e. it wasn't missing)
2295
# then we can't return False, we need to preserve errors
2298
if ret_false and preserve_errors and out:
2299
# If we are preserving errors, but all
2300
# the failures are from missing sections / values
2301
# then we can return False. Otherwise there is a
2302
# real failure that we need to preserve.
2303
ret_false = not any(out.values())
2312
"""Clear ConfigObj instance and restore to 'freshly created' state."""
2315
# FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2316
# requires an empty dictionary
2317
self.configspec = None
2318
# Just to be sure ;-)
2319
self._original_configspec = None
2324
Reload a ConfigObj from file.
2326
This method raises a ``ReloadError`` if the ConfigObj doesn't have
2327
a filename attribute pointing to a file.
2329
if not isinstance(self.filename, basestring):
2332
filename = self.filename
2333
current_options = {}
2334
for entry in OPTION_DEFAULTS:
2335
if entry == 'configspec':
2337
current_options[entry] = getattr(self, entry)
2339
configspec = self._original_configspec
2340
current_options['configspec'] = configspec
2343
self._initialise(current_options)
2344
self._load(filename, configspec)
2348
class SimpleVal(object):
2351
Can be used to check that all members expected are present.
2353
To use it, provide a configspec with all your members in (the value given
2354
will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2355
method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2356
members are present, or a dictionary with True/False meaning
2357
present/missing. (Whole missing sections will be replaced with ``False``)
2361
self.baseErrorClass = ConfigObjError
2363
def check(self, check, member, missing=False):
2364
"""A dummy check method, always returns the value unchanged."""
2366
raise self.baseErrorClass()
2370
def flatten_errors(cfg, res, levels=None, results=None):
2372
An example function that will turn a nested dictionary of results
2373
(as returned by ``ConfigObj.validate``) into a flat list.
2375
``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2376
dictionary returned by ``validate``.
2378
(This is a recursive function, so you shouldn't use the ``levels`` or
2379
``results`` arguments - they are used by the function.)
2381
Returns a list of keys that failed. Each member of the list is a tuple::
2383
([list of sections...], key, result)
2385
If ``validate`` was called with ``preserve_errors=False`` (the default)
2386
then ``result`` will always be ``False``.
2388
*list of sections* is a flattened list of sections that the key was found
2391
If the section was missing (or a section was expected and a scalar provided
2392
- or vice-versa) then key will be ``None``.
2394
If the value (or section) was missing then ``result`` will be ``False``.
2396
If ``validate`` was called with ``preserve_errors=True`` and a value
2397
was present, but failed the check, then ``result`` will be the exception
2398
object returned. You can use this as a string that describes the failure.
2400
For example *The value "3" is of the wrong type*.
2408
if res == False or isinstance(res, Exception):
2409
results.append((levels[:], None, res))
2413
for (key, val) in res.items():
2416
if isinstance(cfg.get(key), dict):
2419
flatten_errors(cfg[key], val, levels, results)
2421
results.append((levels[:], key, val))
2430
def get_extra_values(conf, _prepend=()):
2432
Find all the values and sections not in the configspec from a validated
2435
``get_extra_values`` returns a list of tuples where each tuple represents
2436
either an extra section, or an extra value.
2438
The tuples contain two values, a tuple representing the section the value
2439
is in and the name of the extra values. For extra values in the top level
2440
section the first member will be an empty tuple. For values in the 'foo'
2441
section the first member will be ``('foo',)``. For members in the 'bar'
2442
subsection of the 'foo' section the first member will be ``('foo', 'bar')``.
2444
NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't
2445
been validated it will return an empty list.
2449
out.extend([(_prepend, name) for name in conf.extra_values])
2450
for name in conf.sections:
2451
if name not in conf.extra_values:
2452
out.extend(get_extra_values(conf[name], _prepend + (name,)))
2456
"""*A programming language is a medium of expression.* - Paul Graham"""