2
# A config file reader/writer that supports nested sections in config files.
3
# Copyright (C) 2005-2009 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.
20
from __future__ import generators
27
# Bzr modification: Disabled import of 'compiler' module
28
# bzr doesn't use the 'unrepr' feature of configobj, so importing compiler just
29
# wastes several milliseconds on every single bzr invocation.
30
# -- Andrew Bennetts, 2008-10-14
39
from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
41
# Python 2.2 does not have these
43
BOM_UTF8 = '\xef\xbb\xbf'
44
# UTF-16, little endian
45
BOM_UTF16_LE = '\xff\xfe'
47
BOM_UTF16_BE = '\xfe\xff'
48
if sys.byteorder == 'little':
49
# UTF-16, native endianness
50
BOM_UTF16 = BOM_UTF16_LE
52
# UTF-16, native endianness
53
BOM_UTF16 = BOM_UTF16_BE
55
# A dictionary mapping BOM to
56
# the encoding to decode with, and what to set the
57
# encoding attribute to.
59
BOM_UTF8: ('utf_8', None),
60
BOM_UTF16_BE: ('utf16_be', 'utf_16'),
61
BOM_UTF16_LE: ('utf16_le', 'utf_16'),
62
BOM_UTF16: ('utf_16', 'utf_16'),
64
# All legal variants of the BOM codecs.
65
# TODO: the list of aliases is not meant to be exhaustive, is there a
72
'utf16_be': 'utf16_be',
73
'utf_16_be': 'utf16_be',
74
'utf-16be': 'utf16_be',
75
'utf16_le': 'utf16_le',
76
'utf_16_le': 'utf16_le',
77
'utf-16le': 'utf16_le',
85
# Map of encodings to the BOM to write.
89
'utf16_be': BOM_UTF16_BE,
90
'utf16_le': BOM_UTF16_LE,
95
def match_utf8(encoding):
96
return BOM_LIST.get(encoding.lower()) == 'utf_8'
99
# Quote strings used for writing values
103
wspace_plus = ' \r\n\v\t\'"'
111
"""enumerate for Python 2.2."""
117
# Sentinel for use in getattr calls to replace hasattr
120
__version__ = '4.6.0'
122
__revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
124
__docformat__ = "restructuredtext en"
128
'DEFAULT_INDENT_TYPE',
129
'DEFAULT_INTERPOLATION',
137
'InterpolationError',
138
'InterpolationLoopError',
139
'MissingInterpolationOption',
140
'RepeatSectionError',
148
DEFAULT_INTERPOLATION = 'configparser'
149
DEFAULT_INDENT_TYPE = ' '
150
MAX_INTERPOL_DEPTH = 10
153
'interpolation': True,
154
'raise_errors': False,
156
'create_empty': False,
160
# option may be set to one of ('', ' ', '\t')
163
'default_encoding': None,
165
'write_empty_values': False,
173
raise ImportError('compiler module not available')
174
p = compiler.parse(s)
175
return p.getChildren()[1].getChildren()[0].getChildren()[1]
178
class UnknownType(Exception):
182
class Builder(object):
185
m = getattr(self, 'build_' + o.__class__.__name__, None)
187
raise UnknownType(o.__class__.__name__)
190
def build_List(self, o):
191
return map(self.build, o.getChildren())
193
def build_Const(self, o):
196
def build_Dict(self, o):
198
i = iter(map(self.build, o.getChildren()))
203
def build_Tuple(self, o):
204
return tuple(self.build_List(o))
206
def build_Name(self, o):
211
if o.name == 'False':
215
raise UnknownType('Undefined Name')
217
def build_Add(self, o):
218
real, imag = map(self.build_Const, o.getChildren())
222
raise UnknownType('Add')
223
if not isinstance(imag, complex) or imag.real != 0.0:
224
raise UnknownType('Add')
227
def build_Getattr(self, o):
228
parent = self.build(o.expr)
229
return getattr(parent, o.attrname)
231
def build_UnarySub(self, o):
232
return -self.build_Const(o.getChildren()[0])
234
def build_UnaryAdd(self, o):
235
return self.build_Const(o.getChildren()[0])
244
return _builder.build(getObj(s))
248
class ConfigObjError(SyntaxError):
250
This is the base class for all errors that ConfigObj raises.
251
It is a subclass of SyntaxError.
253
def __init__(self, message='', line_number=None, line=''):
255
self.line_number = line_number
256
SyntaxError.__init__(self, message)
259
class NestingError(ConfigObjError):
261
This error indicates a level of nesting that doesn't match.
265
class ParseError(ConfigObjError):
267
This error indicates that a line is badly written.
268
It is neither a valid ``key = value`` line,
269
nor a valid section marker line.
273
class ReloadError(IOError):
275
A 'reload' operation failed.
276
This exception is a subclass of ``IOError``.
279
IOError.__init__(self, 'reload failed, filename is not set.')
282
class DuplicateError(ConfigObjError):
284
The keyword or section specified already exists.
288
class ConfigspecError(ConfigObjError):
290
An error occured whilst parsing a configspec.
294
class InterpolationError(ConfigObjError):
295
"""Base class for the two interpolation errors."""
298
class InterpolationLoopError(InterpolationError):
299
"""Maximum interpolation depth exceeded in string interpolation."""
301
def __init__(self, option):
302
InterpolationError.__init__(
304
'interpolation loop detected in value "%s".' % option)
307
class RepeatSectionError(ConfigObjError):
309
This error indicates additional sections in a section with a
310
``__many__`` (repeated) section.
314
class MissingInterpolationOption(InterpolationError):
315
"""A value specified for interpolation was missing."""
317
def __init__(self, option):
318
InterpolationError.__init__(
320
'missing option "%s" in interpolation.' % option)
323
class UnreprError(ConfigObjError):
324
"""An error parsing in unrepr mode."""
328
class InterpolationEngine(object):
330
A helper class to help perform string interpolation.
332
This class is an abstract base class; its descendants perform
336
# compiled regexp to use in self.interpolate()
337
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
339
def __init__(self, section):
340
# the Section instance that "owns" this engine
341
self.section = section
344
def interpolate(self, key, value):
345
def recursive_interpolate(key, value, section, backtrail):
346
"""The function that does the actual work.
348
``value``: the string we're trying to interpolate.
349
``section``: the section in which that string was found
350
``backtrail``: a dict to keep track of where we've been,
351
to detect and prevent infinite recursion loops
353
This is similar to a depth-first-search algorithm.
355
# Have we been here already?
356
if (key, section.name) in backtrail:
357
# Yes - infinite loop detected
358
raise InterpolationLoopError(key)
359
# Place a marker on our backtrail so we won't come back here again
360
backtrail[(key, section.name)] = 1
362
# Now start the actual work
363
match = self._KEYCRE.search(value)
365
# The actual parsing of the match is implementation-dependent,
366
# so delegate to our helper function
367
k, v, s = self._parse_match(match)
369
# That's the signal that no further interpolation is needed
372
# Further interpolation may be needed to obtain final value
373
replacement = recursive_interpolate(k, v, s, backtrail)
374
# Replace the matched string with its final value
375
start, end = match.span()
376
value = ''.join((value[:start], replacement, value[end:]))
377
new_search_start = start + len(replacement)
378
# Pick up the next interpolation key, if any, for next time
379
# through the while loop
380
match = self._KEYCRE.search(value, new_search_start)
382
# Now safe to come back here again; remove marker from backtrail
383
del backtrail[(key, section.name)]
387
# Back in interpolate(), all we have to do is kick off the recursive
388
# function with appropriate starting values
389
value = recursive_interpolate(key, value, self.section, {})
393
def _fetch(self, key):
394
"""Helper function to fetch values from owning section.
396
Returns a 2-tuple: the value, and the section where it was found.
398
# switch off interpolation before we try and fetch anything !
399
save_interp = self.section.main.interpolation
400
self.section.main.interpolation = False
402
# Start at section that "owns" this InterpolationEngine
403
current_section = self.section
405
# try the current section first
406
val = current_section.get(key)
410
val = current_section.get('DEFAULT', {}).get(key)
413
# move up to parent and try again
414
# top-level's parent is itself
415
if current_section.parent is current_section:
416
# reached top level, time to give up
418
current_section = current_section.parent
420
# restore interpolation to previous value before returning
421
self.section.main.interpolation = save_interp
423
raise MissingInterpolationOption(key)
424
return val, current_section
427
def _parse_match(self, match):
428
"""Implementation-dependent helper function.
430
Will be passed a match object corresponding to the interpolation
431
key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
432
key in the appropriate config file section (using the ``_fetch()``
433
helper function) and return a 3-tuple: (key, value, section)
435
``key`` is the name of the key we're looking for
436
``value`` is the value found for that key
437
``section`` is a reference to the section where it was found
439
``key`` and ``section`` should be None if no further
440
interpolation should be performed on the resulting value
441
(e.g., if we interpolated "$$" and returned "$").
443
raise NotImplementedError()
447
class ConfigParserInterpolation(InterpolationEngine):
448
"""Behaves like ConfigParser."""
449
_KEYCRE = re.compile(r"%\(([^)]*)\)s")
451
def _parse_match(self, match):
453
value, section = self._fetch(key)
454
return key, value, section
458
class TemplateInterpolation(InterpolationEngine):
459
"""Behaves like string.Template."""
461
_KEYCRE = re.compile(r"""
463
(?P<escaped>\$) | # Two $ signs
464
(?P<named>[_a-z][_a-z0-9]*) | # $name format
465
{(?P<braced>[^}]*)} # ${name} format
467
""", re.IGNORECASE | re.VERBOSE)
469
def _parse_match(self, match):
470
# Valid name (in or out of braces): fetch value from section
471
key = match.group('named') or match.group('braced')
473
value, section = self._fetch(key)
474
return key, value, section
475
# Escaped delimiter (e.g., $$): return single delimiter
476
if match.group('escaped') is not None:
477
# Return None for key and section to indicate it's time to stop
478
return None, self._delimiter, None
479
# Anything else: ignore completely, just return it unchanged
480
return None, match.group(), None
483
interpolation_engines = {
484
'configparser': ConfigParserInterpolation,
485
'template': TemplateInterpolation,
489
def __newobj__(cls, *args):
491
return cls.__new__(cls, *args)
495
A dictionary-like object that represents a section in a config file.
497
It does string interpolation if the 'interpolation' attribute
498
of the 'main' object is set to True.
500
Interpolation is tried first from this object, then from the 'DEFAULT'
501
section of this object, next from the parent and its 'DEFAULT' section,
502
and so on until the main object is reached.
504
A Section will behave like an ordered dictionary - following the
505
order of the ``scalars`` and ``sections`` attributes.
506
You can use this to change the order of members.
508
Iteration follows the order: scalars, then sections.
512
def __setstate__(self, state):
513
dict.update(self, state[0])
514
self.__dict__.update(state[1])
516
def __reduce__(self):
517
state = (dict(self), self.__dict__)
518
return (__newobj__, (self.__class__,), state)
521
def __init__(self, parent, depth, main, indict=None, name=None):
523
* parent is the section above
524
* depth is the depth level of this section
525
* main is the main ConfigObj
526
* indict is a dictionary to initialise the section with
531
# used for nesting level *and* interpolation
533
# used for the interpolation attribute
535
# level of nesting depth of this Section
537
# purely for information
541
# we do this explicitly so that __setitem__ is used properly
542
# (rather than just passing to ``dict.__init__``)
543
for entry, value in indict.iteritems():
547
def _initialise(self):
548
# the sequence of scalar values in this Section
550
# the sequence of sections in this Section
554
self.inline_comments = {}
556
self.configspec = None
559
self.default_values = {}
562
def _interpolate(self, key, value):
564
# do we already have an interpolation engine?
565
engine = self._interpolation_engine
566
except AttributeError:
567
# not yet: first time running _interpolate(), so pick the engine
568
name = self.main.interpolation
569
if name == True: # note that "if name:" would be incorrect here
570
# backwards-compatibility: interpolation=True means use default
571
name = DEFAULT_INTERPOLATION
572
name = name.lower() # so that "Template", "template", etc. all work
573
class_ = interpolation_engines.get(name, None)
575
# invalid value for self.main.interpolation
576
self.main.interpolation = False
579
# save reference to engine so we don't have to do this again
580
engine = self._interpolation_engine = class_(self)
581
# let the engine do the actual work
582
return engine.interpolate(key, value)
585
def __getitem__(self, key):
586
"""Fetch the item and do string interpolation."""
587
val = dict.__getitem__(self, key)
588
if self.main.interpolation and isinstance(val, basestring):
589
return self._interpolate(key, val)
593
def __setitem__(self, key, value, unrepr=False):
595
Correctly set a value.
597
Making dictionary values Section instances.
598
(We have to special case 'Section' instances - which are also dicts)
600
Keys must be strings.
601
Values need only be strings (or lists of strings) if
602
``main.stringify`` is set.
604
``unrepr`` must be set when setting a value to a dictionary, without
605
creating a new sub-section.
607
if not isinstance(key, basestring):
608
raise ValueError('The key "%s" is not a string.' % key)
611
if key not in self.comments:
612
self.comments[key] = []
613
self.inline_comments[key] = ''
614
# remove the entry from defaults
615
if key in self.defaults:
616
self.defaults.remove(key)
618
if isinstance(value, Section):
620
self.sections.append(key)
621
dict.__setitem__(self, key, value)
622
elif isinstance(value, dict) and not unrepr:
623
# First create the new depth level,
624
# then create the section
626
self.sections.append(key)
627
new_depth = self.depth + 1
639
self.scalars.append(key)
640
if not self.main.stringify:
641
if isinstance(value, basestring):
643
elif isinstance(value, (list, tuple)):
645
if not isinstance(entry, basestring):
646
raise TypeError('Value is not a string "%s".' % entry)
648
raise TypeError('Value is not a string "%s".' % value)
649
dict.__setitem__(self, key, value)
652
def __delitem__(self, key):
653
"""Remove items from the sequence when deleting."""
654
dict. __delitem__(self, key)
655
if key in self.scalars:
656
self.scalars.remove(key)
658
self.sections.remove(key)
659
del self.comments[key]
660
del self.inline_comments[key]
663
def get(self, key, default=None):
664
"""A version of ``get`` that doesn't bypass string interpolation."""
671
def update(self, indict):
673
A version of update that uses our ``__setitem__``.
676
self[entry] = indict[entry]
679
def pop(self, key, *args):
681
'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
682
If key is not found, d is returned if given, otherwise KeyError is raised'
684
val = dict.pop(self, key, *args)
685
if key in self.scalars:
686
del self.comments[key]
687
del self.inline_comments[key]
688
self.scalars.remove(key)
689
elif key in self.sections:
690
del self.comments[key]
691
del self.inline_comments[key]
692
self.sections.remove(key)
693
if self.main.interpolation and isinstance(val, basestring):
694
return self._interpolate(key, val)
699
"""Pops the first (key,val)"""
700
sequence = (self.scalars + self.sections)
702
raise KeyError(": 'popitem(): dictionary is empty'")
711
A version of clear that also affects scalars/sections
712
Also clears comments and configspec.
714
Leaves other attributes alone :
715
depth/main/parent are not affected
721
self.inline_comments = {}
722
self.configspec = None
725
def setdefault(self, key, default=None):
726
"""A version of setdefault that sets sequence if appropriate."""
735
"""D.items() -> list of D's (key, value) pairs, as 2-tuples"""
736
return zip((self.scalars + self.sections), self.values())
740
"""D.keys() -> list of D's keys"""
741
return (self.scalars + self.sections)
745
"""D.values() -> list of D's values"""
746
return [self[key] for key in (self.scalars + self.sections)]
750
"""D.iteritems() -> an iterator over the (key, value) items of D"""
751
return iter(self.items())
755
"""D.iterkeys() -> an iterator over the keys of D"""
756
return iter((self.scalars + self.sections))
761
def itervalues(self):
762
"""D.itervalues() -> an iterator over the values of D"""
763
return iter(self.values())
767
"""x.__repr__() <==> repr(x)"""
768
return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
769
for key in (self.scalars + self.sections)])
772
__str__.__doc__ = "x.__str__() <==> str(x)"
775
# Extra methods - not in a normal dictionary
779
Return a deepcopy of self as a dictionary.
781
All members that are ``Section`` instances are recursively turned to
782
ordinary dictionaries - by calling their ``dict`` method.
792
this_entry = self[entry]
793
if isinstance(this_entry, Section):
794
this_entry = this_entry.dict()
795
elif isinstance(this_entry, list):
796
# create a copy rather than a reference
797
this_entry = list(this_entry)
798
elif isinstance(this_entry, tuple):
799
# create a copy rather than a reference
800
this_entry = tuple(this_entry)
801
newdict[entry] = this_entry
805
def merge(self, indict):
807
A recursive update - useful for merging config files.
809
>>> a = '''[section1]
812
... more_options = False
813
... # end of file'''.splitlines()
814
>>> b = '''# File is user.ini
817
... # end of file'''.splitlines()
818
>>> c1 = ConfigObj(b)
819
>>> c2 = ConfigObj(a)
822
ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
824
for key, val in indict.items():
825
if (key in self and isinstance(self[key], dict) and
826
isinstance(val, dict)):
832
def rename(self, oldkey, newkey):
834
Change a keyname to another, without changing position in sequence.
836
Implemented so that transformations can be made on keys,
837
as well as on values. (used by encode and decode)
839
Also renames comments.
841
if oldkey in self.scalars:
842
the_list = self.scalars
843
elif oldkey in self.sections:
844
the_list = self.sections
846
raise KeyError('Key "%s" not found.' % oldkey)
847
pos = the_list.index(oldkey)
850
dict.__delitem__(self, oldkey)
851
dict.__setitem__(self, newkey, val)
852
the_list.remove(oldkey)
853
the_list.insert(pos, newkey)
854
comm = self.comments[oldkey]
855
inline_comment = self.inline_comments[oldkey]
856
del self.comments[oldkey]
857
del self.inline_comments[oldkey]
858
self.comments[newkey] = comm
859
self.inline_comments[newkey] = inline_comment
862
def walk(self, function, raise_errors=True,
863
call_on_sections=False, **keywargs):
865
Walk every member and call a function on the keyword and value.
867
Return a dictionary of the return values
869
If the function raises an exception, raise the errror
870
unless ``raise_errors=False``, in which case set the return value to
873
Any unrecognised keyword arguments you pass to walk, will be pased on
874
to the function you pass in.
876
Note: if ``call_on_sections`` is ``True`` then - on encountering a
877
subsection, *first* the function is called for the *whole* subsection,
878
and then recurses into it's members. This means your function must be
879
able to handle strings, dictionaries and lists. This allows you
880
to change the key of subsections as well as for ordinary members. The
881
return value when called on the whole subsection has to be discarded.
883
See the encode and decode methods for examples, including functions.
885
.. admonition:: caution
887
You can use ``walk`` to transform the names of members of a section
888
but you mustn't add or delete members.
890
>>> config = '''[XXXXsection]
891
... XXXXkey = XXXXvalue'''.splitlines()
892
>>> cfg = ConfigObj(config)
894
ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
895
>>> def transform(section, key):
896
... val = section[key]
897
... newkey = key.replace('XXXX', 'CLIENT1')
898
... section.rename(key, newkey)
899
... if isinstance(val, (tuple, list, dict)):
902
... val = val.replace('XXXX', 'CLIENT1')
903
... section[newkey] = val
904
>>> cfg.walk(transform, call_on_sections=True)
905
{'CLIENT1section': {'CLIENT1key': None}}
907
ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
911
for i in range(len(self.scalars)):
912
entry = self.scalars[i]
914
val = function(self, entry, **keywargs)
915
# bound again in case name has changed
916
entry = self.scalars[i]
922
entry = self.scalars[i]
925
for i in range(len(self.sections)):
926
entry = self.sections[i]
929
function(self, entry, **keywargs)
934
entry = self.sections[i]
936
# bound again in case name has changed
937
entry = self.sections[i]
938
# previous result is discarded
939
out[entry] = self[entry].walk(
941
raise_errors=raise_errors,
942
call_on_sections=call_on_sections,
947
def as_bool(self, key):
949
Accepts a key as input. The corresponding value must be a string or
950
the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
951
retain compatibility with Python 2.2.
953
If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
956
If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
959
``as_bool`` is not case sensitive.
961
Any other input will raise a ``ValueError``.
966
Traceback (most recent call last):
967
ValueError: Value "fish" is neither True nor False
982
if not isinstance(val, basestring):
983
# TODO: Why do we raise a KeyError here?
986
return self.main._bools[val.lower()]
988
raise ValueError('Value "%s" is neither True nor False' % val)
991
def as_int(self, key):
993
A convenience method which coerces the specified value to an integer.
995
If the value is an invalid literal for ``int``, a ``ValueError`` will
1001
Traceback (most recent call last):
1002
ValueError: invalid literal for int() with base 10: 'fish'
1008
Traceback (most recent call last):
1009
ValueError: invalid literal for int() with base 10: '3.2'
1011
return int(self[key])
1014
def as_float(self, key):
1016
A convenience method which coerces the specified value to a float.
1018
If the value is an invalid literal for ``float``, a ``ValueError`` will
1024
Traceback (most recent call last):
1025
ValueError: invalid literal for float(): fish
1033
return float(self[key])
1036
def as_list(self, key):
1038
A convenience method which fetches the specified value, guaranteeing
1053
if isinstance(result, (tuple, list)):
1058
def restore_default(self, key):
1060
Restore (and return) default value for the specified key.
1062
This method will only work for a ConfigObj that was created
1063
with a configspec and has been validated.
1065
If there is no default value for this key, ``KeyError`` is raised.
1067
default = self.default_values[key]
1068
dict.__setitem__(self, key, default)
1069
if key not in self.defaults:
1070
self.defaults.append(key)
1074
def restore_defaults(self):
1076
Recursively restore default values to all members
1079
This method will only work for a ConfigObj that was created
1080
with a configspec and has been validated.
1082
It doesn't delete or modify entries without default values.
1084
for key in self.default_values:
1085
self.restore_default(key)
1087
for section in self.sections:
1088
self[section].restore_defaults()
1091
class ConfigObj(Section):
1092
"""An object to read, create, and write config files."""
1094
_keyword = re.compile(r'''^ # line start
1097
(?:".*?")| # double quotes
1098
(?:'.*?')| # single quotes
1099
(?:[^'"=].*?) # no quotes
1102
(.*) # value (including list values and comments)
1107
_sectionmarker = re.compile(r'''^
1108
(\s*) # 1: indentation
1109
((?:\[\s*)+) # 2: section marker open
1110
( # 3: section name open
1111
(?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1112
(?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1113
(?:[^'"\s].*?) # at least one non-space unquoted
1114
) # section name close
1115
((?:\s*\])+) # 4: section marker close
1116
\s*(\#.*)? # 5: optional comment
1120
# this regexp pulls list values out as a single string
1121
# or single values and comments
1122
# FIXME: this regex adds a '' to the end of comma terminated lists
1123
# workaround in ``_handle_value``
1124
_valueexp = re.compile(r'''^
1130
(?:".*?")| # double quotes
1131
(?:'.*?')| # single quotes
1132
(?:[^'",\#][^,\#]*?) # unquoted
1135
)* # match all list items ending in a comma (if any)
1138
(?:".*?")| # double quotes
1139
(?:'.*?')| # single quotes
1140
(?:[^'",\#\s][^,]*?)| # unquoted
1141
(?:(?<!,)) # Empty value
1142
)? # last item in a list - or string value
1144
(,) # alternatively a single comma - empty list
1146
\s*(\#.*)? # optional comment
1150
# use findall to get the members of a list value
1151
_listvalueexp = re.compile(r'''
1153
(?:".*?")| # double quotes
1154
(?:'.*?')| # single quotes
1155
(?:[^'",\#].*?) # unquoted
1161
# this regexp is used for the value
1162
# when lists are switched off
1163
_nolistvalue = re.compile(r'''^
1165
(?:".*?")| # double quotes
1166
(?:'.*?')| # single quotes
1167
(?:[^'"\#].*?)| # unquoted
1170
\s*(\#.*)? # optional comment
1174
# regexes for finding triple quoted values on one line
1175
_single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1176
_single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1177
_multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1178
_multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1181
"'''": (_single_line_single, _multi_line_single),
1182
'"""': (_single_line_double, _multi_line_double),
1185
# Used by the ``istrue`` Section method
1187
'yes': True, 'no': False,
1188
'on': True, 'off': False,
1189
'1': True, '0': False,
1190
'true': True, 'false': False,
1194
def __init__(self, infile=None, options=None, _inspec=False, **kwargs):
1196
Parse a config file or create a config file object.
1198
``ConfigObj(infile=None, options=None, **kwargs)``
1200
self._inspec = _inspec
1201
# init the superclass
1202
Section.__init__(self, self, 0, self)
1204
infile = infile or []
1205
options = dict(options or {})
1207
# keyword arguments take precedence over an options dictionary
1208
options.update(kwargs)
1210
options['list_values'] = False
1212
defaults = OPTION_DEFAULTS.copy()
1213
# TODO: check the values too.
1214
for entry in options:
1215
if entry not in defaults:
1216
raise TypeError('Unrecognised option "%s".' % entry)
1218
# Add any explicit options to the defaults
1219
defaults.update(options)
1220
self._initialise(defaults)
1221
configspec = defaults['configspec']
1222
self._original_configspec = configspec
1223
self._load(infile, configspec)
1226
def _load(self, infile, configspec):
1227
if isinstance(infile, basestring):
1228
self.filename = infile
1229
if os.path.isfile(infile):
1230
h = open(infile, 'rb')
1231
infile = h.read() or []
1233
elif self.file_error:
1234
# raise an error if the file doesn't exist
1235
raise IOError('Config file not found: "%s".' % self.filename)
1237
# file doesn't already exist
1238
if self.create_empty:
1239
# this is a good test that the filename specified
1240
# isn't impossible - like on a non-existent device
1241
h = open(infile, 'w')
1246
elif isinstance(infile, (list, tuple)):
1247
infile = list(infile)
1249
elif isinstance(infile, dict):
1251
# the Section class handles creating subsections
1252
if isinstance(infile, ConfigObj):
1253
# get a copy of our ConfigObj
1254
infile = infile.dict()
1256
for entry in infile:
1257
self[entry] = infile[entry]
1260
if configspec is not None:
1261
self._handle_configspec(configspec)
1263
self.configspec = None
1266
elif getattr(infile, 'read', MISSING) is not MISSING:
1267
# This supports file like objects
1268
infile = infile.read() or []
1269
# needs splitting into lines - but needs doing *after* decoding
1270
# in case it's not an 8 bit encoding
1272
raise TypeError('infile must be a filename, file like object, or list of lines.')
1275
# don't do it for the empty ConfigObj
1276
infile = self._handle_bom(infile)
1277
# infile is now *always* a list
1279
# Set the newlines attribute (first line ending it finds)
1280
# and strip trailing '\n' or '\r' from lines
1282
if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1284
for end in ('\r\n', '\n', '\r'):
1285
if line.endswith(end):
1290
infile = [line.rstrip('\r\n') for line in infile]
1293
# if we had any errors, now is the time to raise them
1295
info = "at line %s." % self._errors[0].line_number
1296
if len(self._errors) > 1:
1297
msg = "Parsing failed with several errors.\nFirst error %s" % info
1298
error = ConfigObjError(msg)
1300
error = self._errors[0]
1301
# set the errors attribute; it's a list of tuples:
1302
# (error_type, message, line_number)
1303
error.errors = self._errors
1304
# set the config attribute
1307
# delete private attributes
1310
if configspec is None:
1311
self.configspec = None
1313
self._handle_configspec(configspec)
1316
def _initialise(self, options=None):
1318
options = OPTION_DEFAULTS
1320
# initialise a few variables
1321
self.filename = None
1323
self.raise_errors = options['raise_errors']
1324
self.interpolation = options['interpolation']
1325
self.list_values = options['list_values']
1326
self.create_empty = options['create_empty']
1327
self.file_error = options['file_error']
1328
self.stringify = options['stringify']
1329
self.indent_type = options['indent_type']
1330
self.encoding = options['encoding']
1331
self.default_encoding = options['default_encoding']
1333
self.newlines = None
1334
self.write_empty_values = options['write_empty_values']
1335
self.unrepr = options['unrepr']
1337
self.initial_comment = []
1338
self.final_comment = []
1339
self.configspec = None
1342
self.list_values = False
1344
# Clear section attributes as well
1345
Section._initialise(self)
1349
return ('ConfigObj({%s})' %
1350
', '.join([('%s: %s' % (repr(key), repr(self[key])))
1351
for key in (self.scalars + self.sections)]))
1354
def _handle_bom(self, infile):
1356
Handle any BOM, and decode if necessary.
1358
If an encoding is specified, that *must* be used - but the BOM should
1359
still be removed (and the BOM attribute set).
1361
(If the encoding is wrongly specified, then a BOM for an alternative
1362
encoding won't be discovered or removed.)
1364
If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1365
removed. The BOM attribute will be set. UTF16 will be decoded to
1368
NOTE: This method must not be called with an empty ``infile``.
1370
Specifying the *wrong* encoding is likely to cause a
1371
``UnicodeDecodeError``.
1373
``infile`` must always be returned as a list of lines, but may be
1374
passed in as a single string.
1376
if ((self.encoding is not None) and
1377
(self.encoding.lower() not in BOM_LIST)):
1378
# No need to check for a BOM
1379
# the encoding specified doesn't have one
1381
return self._decode(infile, self.encoding)
1383
if isinstance(infile, (list, tuple)):
1387
if self.encoding is not None:
1388
# encoding explicitly supplied
1389
# And it could have an associated BOM
1390
# TODO: if encoding is just UTF16 - we ought to check for both
1391
# TODO: big endian and little endian versions.
1392
enc = BOM_LIST[self.encoding.lower()]
1394
# For UTF16 we try big endian and little endian
1395
for BOM, (encoding, final_encoding) in BOMS.items():
1396
if not final_encoding:
1399
if infile.startswith(BOM):
1402
# Don't need to remove BOM
1403
return self._decode(infile, encoding)
1405
# If we get this far, will *probably* raise a DecodeError
1406
# As it doesn't appear to start with a BOM
1407
return self._decode(infile, self.encoding)
1411
if not line.startswith(BOM):
1412
return self._decode(infile, self.encoding)
1414
newline = line[len(BOM):]
1417
if isinstance(infile, (list, tuple)):
1422
return self._decode(infile, self.encoding)
1424
# No encoding specified - so we need to check for UTF8/UTF16
1425
for BOM, (encoding, final_encoding) in BOMS.items():
1426
if not line.startswith(BOM):
1430
self.encoding = final_encoding
1431
if not final_encoding:
1435
newline = line[len(BOM):]
1436
if isinstance(infile, (list, tuple)):
1440
# UTF8 - don't decode
1441
if isinstance(infile, basestring):
1442
return infile.splitlines(True)
1445
# UTF16 - have to decode
1446
return self._decode(infile, encoding)
1448
# No BOM discovered and no encoding specified, just return
1449
if isinstance(infile, basestring):
1450
# infile read from a file will be a single string
1451
return infile.splitlines(True)
1455
def _a_to_u(self, aString):
1456
"""Decode ASCII strings to unicode if a self.encoding is specified."""
1458
return aString.decode('ascii')
1463
def _decode(self, infile, encoding):
1465
Decode infile to unicode. Using the specified encoding.
1467
if is a string, it also needs converting to a list.
1469
if isinstance(infile, basestring):
1471
# NOTE: Could raise a ``UnicodeDecodeError``
1472
return infile.decode(encoding).splitlines(True)
1473
for i, line in enumerate(infile):
1474
if not isinstance(line, unicode):
1475
# NOTE: The isinstance test here handles mixed lists of unicode/string
1476
# NOTE: But the decode will break on any non-string values
1477
# NOTE: Or could raise a ``UnicodeDecodeError``
1478
infile[i] = line.decode(encoding)
1482
def _decode_element(self, line):
1483
"""Decode element to unicode if necessary."""
1484
if not self.encoding:
1486
if isinstance(line, str) and self.default_encoding:
1487
return line.decode(self.default_encoding)
1491
def _str(self, value):
1493
Used by ``stringify`` within validate, to turn non-string values
1496
if not isinstance(value, basestring):
1502
def _parse(self, infile):
1503
"""Actually parse the config file."""
1504
temp_list_values = self.list_values
1506
self.list_values = False
1511
maxline = len(infile) - 1
1513
reset_comment = False
1515
while cur_index < maxline:
1519
line = infile[cur_index]
1520
sline = line.strip()
1521
# do we have anything on the line ?
1522
if not sline or sline.startswith('#'):
1523
reset_comment = False
1524
comment_list.append(line)
1528
# preserve initial comment
1529
self.initial_comment = comment_list
1533
reset_comment = True
1534
# first we check if it's a section marker
1535
mat = self._sectionmarker.match(line)
1538
(indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1539
if indent and (self.indent_type is None):
1540
self.indent_type = indent
1541
cur_depth = sect_open.count('[')
1542
if cur_depth != sect_close.count(']'):
1543
self._handle_error("Cannot compute the section depth at line %s.",
1544
NestingError, infile, cur_index)
1547
if cur_depth < this_section.depth:
1548
# the new section is dropping back to a previous level
1550
parent = self._match_depth(this_section,
1553
self._handle_error("Cannot compute nesting level at line %s.",
1554
NestingError, infile, cur_index)
1556
elif cur_depth == this_section.depth:
1557
# the new section is a sibling of the current section
1558
parent = this_section.parent
1559
elif cur_depth == this_section.depth + 1:
1560
# the new section is a child the current section
1561
parent = this_section
1563
self._handle_error("Section too nested at line %s.",
1564
NestingError, infile, cur_index)
1566
sect_name = self._unquote(sect_name)
1567
if sect_name in parent:
1568
self._handle_error('Duplicate section name at line %s.',
1569
DuplicateError, infile, cur_index)
1572
# create the new section
1573
this_section = Section(
1578
parent[sect_name] = this_section
1579
parent.inline_comments[sect_name] = comment
1580
parent.comments[sect_name] = comment_list
1583
# it's not a section marker,
1584
# so it should be a valid ``key = value`` line
1585
mat = self._keyword.match(line)
1587
# it neither matched as a keyword
1588
# or a section marker
1590
'Invalid line at line "%s".',
1591
ParseError, infile, cur_index)
1593
# is a keyword value
1594
# value will include any inline comment
1595
(indent, key, value) = mat.groups()
1596
if indent and (self.indent_type is None):
1597
self.indent_type = indent
1598
# check for a multiline value
1599
if value[:3] in ['"""', "'''"]:
1601
(value, comment, cur_index) = self._multiline(
1602
value, infile, cur_index, maxline)
1605
'Parse error in value at line %s.',
1606
ParseError, infile, cur_index)
1612
value = unrepr(value)
1613
except Exception, e:
1614
if type(e) == UnknownType:
1615
msg = 'Unknown name or type in value at line %s.'
1617
msg = 'Parse error in value at line %s.'
1618
self._handle_error(msg, UnreprError, infile,
1625
value = unrepr(value)
1626
except Exception, e:
1627
if isinstance(e, UnknownType):
1628
msg = 'Unknown name or type in value at line %s.'
1630
msg = 'Parse error in value at line %s.'
1631
self._handle_error(msg, UnreprError, infile,
1635
# extract comment and lists
1637
(value, comment) = self._handle_value(value)
1640
'Parse error in value at line %s.',
1641
ParseError, infile, cur_index)
1644
key = self._unquote(key)
1645
if key in this_section:
1647
'Duplicate keyword name at line %s.',
1648
DuplicateError, infile, cur_index)
1651
# we set unrepr because if we have got this far we will never
1652
# be creating a new section
1653
this_section.__setitem__(key, value, unrepr=True)
1654
this_section.inline_comments[key] = comment
1655
this_section.comments[key] = comment_list
1658
if self.indent_type is None:
1659
# no indentation used, set the type accordingly
1660
self.indent_type = ''
1662
# preserve the final comment
1663
if not self and not self.initial_comment:
1664
self.initial_comment = comment_list
1665
elif not reset_comment:
1666
self.final_comment = comment_list
1667
self.list_values = temp_list_values
1670
def _match_depth(self, sect, depth):
1672
Given a section and a depth level, walk back through the sections
1673
parents to see if the depth level matches a previous section.
1675
Return a reference to the right section,
1676
or raise a SyntaxError.
1678
while depth < sect.depth:
1679
if sect is sect.parent:
1680
# we've reached the top level already
1683
if sect.depth == depth:
1685
# shouldn't get here
1689
def _handle_error(self, text, ErrorClass, infile, cur_index):
1691
Handle an error according to the error settings.
1693
Either raise the error or store it.
1694
The error will have occured at ``cur_index``
1696
line = infile[cur_index]
1698
message = text % cur_index
1699
error = ErrorClass(message, cur_index, line)
1700
if self.raise_errors:
1701
# raise the error - parsing stops here
1704
# reraise when parsing has finished
1705
self._errors.append(error)
1708
def _unquote(self, value):
1709
"""Return an unquoted version of a value"""
1710
if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1715
def _quote(self, value, multiline=True):
1717
Return a safely quoted version of a value.
1719
Raise a ConfigObjError if the value cannot be safely quoted.
1720
If multiline is ``True`` (default) then use triple quotes
1723
* Don't quote values that don't need it.
1724
* Recursively quote members of a list and return a comma joined list.
1725
* Multiline is ``False`` for lists.
1726
* Obey list syntax for empty and single member lists.
1728
If ``list_values=False`` then the value is only quoted if it contains
1729
a ``\\n`` (is multiline) or '#'.
1731
If ``write_empty_values`` is set, and the value is an empty string, it
1734
if multiline and self.write_empty_values and value == '':
1735
# Only if multiline is set, so that it is used for values not
1736
# keys, and not values that are part of a list
1739
if multiline and isinstance(value, (list, tuple)):
1742
elif len(value) == 1:
1743
return self._quote(value[0], multiline=False) + ','
1744
return ', '.join([self._quote(val, multiline=False)
1746
if not isinstance(value, basestring):
1750
raise TypeError('Value "%s" is not a string.' % value)
1755
no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1756
need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1757
hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1758
check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1760
if check_for_single:
1761
if not self.list_values:
1762
# we don't quote if ``list_values=False``
1764
# for normal values either single or double quotes will do
1766
# will only happen if multiline is off - e.g. '\n' in key
1767
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1768
elif ((value[0] not in wspace_plus) and
1769
(value[-1] not in wspace_plus) and
1770
(',' not in value)):
1773
quot = self._get_single_quote(value)
1775
# if value has '\n' or "'" *and* '"', it will need triple quotes
1776
quot = self._get_triple_quote(value)
1778
if quot == noquot and '#' in value and self.list_values:
1779
quot = self._get_single_quote(value)
1784
def _get_single_quote(self, value):
1785
if ("'" in value) and ('"' in value):
1786
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1794
def _get_triple_quote(self, value):
1795
if (value.find('"""') != -1) and (value.find("'''") != -1):
1796
raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1797
if value.find('"""') == -1:
1804
def _handle_value(self, value):
1806
Given a value string, unquote, remove comment,
1807
handle lists. (including empty and single member lists)
1810
# Parsing a configspec so don't handle comments
1812
# do we look for lists in values ?
1813
if not self.list_values:
1814
mat = self._nolistvalue.match(value)
1817
# NOTE: we don't unquote here
1820
mat = self._valueexp.match(value)
1822
# the value is badly constructed, probably badly quoted,
1823
# or an invalid list
1825
(list_values, single, empty_list, comment) = mat.groups()
1826
if (list_values == '') and (single is None):
1827
# change this if you want to accept empty values
1829
# NOTE: note there is no error handling from here if the regex
1830
# is wrong: then incorrect values will slip through
1831
if empty_list is not None:
1832
# the single comma - meaning an empty list
1833
return ([], comment)
1834
if single is not None:
1835
# handle empty values
1836
if list_values and not single:
1837
# FIXME: the '' is a workaround because our regex now matches
1838
# '' at the end of a list if it has a trailing comma
1841
single = single or '""'
1842
single = self._unquote(single)
1843
if list_values == '':
1845
return (single, comment)
1846
the_list = self._listvalueexp.findall(list_values)
1847
the_list = [self._unquote(val) for val in the_list]
1848
if single is not None:
1849
the_list += [single]
1850
return (the_list, comment)
1853
def _multiline(self, value, infile, cur_index, maxline):
1854
"""Extract the value, where we are in a multiline situation."""
1856
newvalue = value[3:]
1857
single_line = self._triple_quote[quot][0]
1858
multi_line = self._triple_quote[quot][1]
1859
mat = single_line.match(value)
1861
retval = list(mat.groups())
1862
retval.append(cur_index)
1864
elif newvalue.find(quot) != -1:
1865
# somehow the triple quote is missing
1868
while cur_index < maxline:
1871
line = infile[cur_index]
1872
if line.find(quot) == -1:
1875
# end of multiline, process it
1878
# we've got to the end of the config, oops...
1880
mat = multi_line.match(line)
1882
# a badly formed line
1884
(value, comment) = mat.groups()
1885
return (newvalue + value, comment, cur_index)
1888
def _handle_configspec(self, configspec):
1889
"""Parse the configspec."""
1890
# FIXME: Should we check that the configspec was created with the
1891
# correct settings ? (i.e. ``list_values=False``)
1892
if not isinstance(configspec, ConfigObj):
1894
configspec = ConfigObj(configspec,
1898
except ConfigObjError, e:
1899
# FIXME: Should these errors have a reference
1900
# to the already parsed ConfigObj ?
1901
raise ConfigspecError('Parsing configspec failed: %s' % e)
1903
raise IOError('Reading configspec failed: %s' % e)
1905
self.configspec = configspec
1909
def _set_configspec(self, section, copy):
1911
Called by validate. Handles setting the configspec on subsections
1912
including sections to be validated by __many__
1914
configspec = section.configspec
1915
many = configspec.get('__many__')
1916
if isinstance(many, dict):
1917
for entry in section.sections:
1918
if entry not in configspec:
1919
section[entry].configspec = many
1921
for entry in configspec.sections:
1922
if entry == '__many__':
1924
if entry not in section:
1928
section.comments[entry] = configspec.comments.get(entry, [])
1929
section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1931
# Could be a scalar when we expect a section
1932
if isinstance(section[entry], Section):
1933
section[entry].configspec = configspec[entry]
1936
def _write_line(self, indent_string, entry, this_entry, comment):
1937
"""Write an individual line, for the write method"""
1938
# NOTE: the calls to self._quote here handles non-StringType values.
1940
val = self._decode_element(self._quote(this_entry))
1942
val = repr(this_entry)
1943
return '%s%s%s%s%s' % (indent_string,
1944
self._decode_element(self._quote(entry, multiline=False)),
1945
self._a_to_u(' = '),
1947
self._decode_element(comment))
1950
def _write_marker(self, indent_string, depth, entry, comment):
1951
"""Write a section marker line"""
1952
return '%s%s%s%s%s' % (indent_string,
1953
self._a_to_u('[' * depth),
1954
self._quote(self._decode_element(entry), multiline=False),
1955
self._a_to_u(']' * depth),
1956
self._decode_element(comment))
1959
def _handle_comment(self, comment):
1960
"""Deal with a comment."""
1963
start = self.indent_type
1964
if not comment.startswith('#'):
1965
start += self._a_to_u(' # ')
1966
return (start + comment)
1971
def write(self, outfile=None, section=None):
1973
Write the current ConfigObj as a file
1975
tekNico: FIXME: use StringIO instead of real files
1977
>>> filename = a.filename
1978
>>> a.filename = 'test.ini'
1980
>>> a.filename = filename
1981
>>> a == ConfigObj('test.ini', raise_errors=True)
1984
if self.indent_type is None:
1985
# this can be true if initialised from a dictionary
1986
self.indent_type = DEFAULT_INDENT_TYPE
1989
cs = self._a_to_u('#')
1990
csp = self._a_to_u('# ')
1992
int_val = self.interpolation
1993
self.interpolation = False
1995
for line in self.initial_comment:
1996
line = self._decode_element(line)
1997
stripped_line = line.strip()
1998
if stripped_line and not stripped_line.startswith(cs):
2002
indent_string = self.indent_type * section.depth
2003
for entry in (section.scalars + section.sections):
2004
if entry in section.defaults:
2005
# don't write out default values
2007
for comment_line in section.comments[entry]:
2008
comment_line = self._decode_element(comment_line.lstrip())
2009
if comment_line and not comment_line.startswith(cs):
2010
comment_line = csp + comment_line
2011
out.append(indent_string + comment_line)
2012
this_entry = section[entry]
2013
comment = self._handle_comment(section.inline_comments[entry])
2015
if isinstance(this_entry, dict):
2017
out.append(self._write_marker(
2022
out.extend(self.write(section=this_entry))
2024
out.append(self._write_line(
2031
for line in self.final_comment:
2032
line = self._decode_element(line)
2033
stripped_line = line.strip()
2034
if stripped_line and not stripped_line.startswith(cs):
2037
self.interpolation = int_val
2039
if section is not self:
2042
if (self.filename is None) and (outfile is None):
2043
# output a list of lines
2044
# might need to encode
2045
# NOTE: This will *screw* UTF16, each line will start with the BOM
2047
out = [l.encode(self.encoding) for l in out]
2048
if (self.BOM and ((self.encoding is None) or
2049
(BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2053
out[0] = BOM_UTF8 + out[0]
2056
# Turn the list to a string, joined with correct newlines
2057
newline = self.newlines or os.linesep
2058
output = self._a_to_u(newline).join(out)
2060
output = output.encode(self.encoding)
2061
if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2063
output = BOM_UTF8 + output
2065
if not output.endswith(newline):
2067
if outfile is not None:
2068
outfile.write(output)
2070
h = open(self.filename, 'wb')
2075
def validate(self, validator, preserve_errors=False, copy=False,
2078
Test the ConfigObj against a configspec.
2080
It uses the ``validator`` object from *validate.py*.
2082
To run ``validate`` on the current ConfigObj, call: ::
2084
test = config.validate(validator)
2086
(Normally having previously passed in the configspec when the ConfigObj
2087
was created - you can dynamically assign a dictionary of checks to the
2088
``configspec`` attribute of a section though).
2090
It returns ``True`` if everything passes, or a dictionary of
2091
pass/fails (True/False). If every member of a subsection passes, it
2092
will just have the value ``True``. (It also returns ``False`` if all
2095
In addition, it converts the values from strings to their native
2096
types if their checks pass (and ``stringify`` is set).
2098
If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2099
of a marking a fail with a ``False``, it will preserve the actual
2100
exception object. This can contain info about the reason for failure.
2101
For example the ``VdtValueTooSmallError`` indicates that the value
2102
supplied was too small. If a value (or section) is missing it will
2103
still be marked as ``False``.
2105
You must have the validate module to use ``preserve_errors=True``.
2107
You can then use the ``flatten_errors`` function to turn your nested
2108
results dictionary into a flattened list of failures - useful for
2109
displaying meaningful error messages.
2112
if self.configspec is None:
2113
raise ValueError('No configspec supplied.')
2115
# We do this once to remove a top level dependency on the validate module
2116
# Which makes importing configobj faster
2117
from validate import VdtMissingValue
2118
self._vdtMissingValue = VdtMissingValue
2123
section.initial_comment = section.configspec.initial_comment
2124
section.final_comment = section.configspec.final_comment
2125
section.encoding = section.configspec.encoding
2126
section.BOM = section.configspec.BOM
2127
section.newlines = section.configspec.newlines
2128
section.indent_type = section.configspec.indent_type
2131
configspec = section.configspec
2132
self._set_configspec(section, copy)
2134
def validate_entry(entry, spec, val, missing, ret_true, ret_false):
2136
check = validator.check(spec,
2140
except validator.baseErrorClass, e:
2141
if not preserve_errors or isinstance(e, self._vdtMissingValue):
2144
# preserve the error
2150
section.default_values.pop(entry, None)
2151
except AttributeError:
2152
# For Python 2.2 compatibility
2154
del section.default_values[entry]
2159
section.default_values[entry] = validator.get_default_value(configspec[entry])
2160
except (KeyError, AttributeError):
2161
# No default or validator has no 'get_default_value' (e.g. SimpleVal)
2166
if self.stringify or missing:
2167
# if we are doing type conversion
2168
# or the value is a supplied default
2169
if not self.stringify:
2170
if isinstance(check, (list, tuple)):
2172
check = [self._str(item) for item in check]
2173
elif missing and check is None:
2174
# convert the None from a default to a ''
2177
check = self._str(check)
2178
if (check != val) or missing:
2179
section[entry] = check
2180
if not copy and missing and entry not in section.defaults:
2181
section.defaults.append(entry)
2182
return ret_true, ret_false
2189
unvalidated = [k for k in section.scalars if k not in configspec]
2190
incorrect_sections = [k for k in configspec.sections if k in section.scalars]
2191
incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2193
for entry in configspec.scalars:
2194
if entry in ('__many__', '___many___'):
2198
if (not entry in section.scalars) or (entry in section.defaults):
2200
# or entries from defaults
2203
if copy and not entry in section.scalars:
2205
section.comments[entry] = (
2206
configspec.comments.get(entry, []))
2207
section.inline_comments[entry] = (
2208
configspec.inline_comments.get(entry, ''))
2212
val = section[entry]
2214
ret_true, ret_false = validate_entry(entry, configspec[entry], val,
2215
missing, ret_true, ret_false)
2218
if '__many__' in configspec.scalars:
2219
many = configspec['__many__']
2220
elif '___many___' in configspec.scalars:
2221
many = configspec['___many___']
2223
if many is not None:
2224
for entry in unvalidated:
2225
val = section[entry]
2226
ret_true, ret_false = validate_entry(entry, many, val, False,
2227
ret_true, ret_false)
2229
for entry in incorrect_scalars:
2231
if not preserve_errors:
2235
msg = 'Value %r was provided as a section' % entry
2236
out[entry] = validator.baseErrorClass(msg)
2237
for entry in incorrect_sections:
2239
if not preserve_errors:
2243
msg = 'Section %r was provided as a single value' % entry
2244
out[entry] = validator.baseErrorClass(msg)
2246
# Missing sections will have been created as empty ones when the
2247
# configspec was read.
2248
for entry in section.sections:
2249
# FIXME: this means DEFAULT is not copied in copy mode
2250
if section is self and entry == 'DEFAULT':
2252
if section[entry].configspec is None:
2255
section.comments[entry] = configspec.comments.get(entry, [])
2256
section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
2257
check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
2275
"""Clear ConfigObj instance and restore to 'freshly created' state."""
2278
# FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2279
# requires an empty dictionary
2280
self.configspec = None
2281
# Just to be sure ;-)
2282
self._original_configspec = None
2287
Reload a ConfigObj from file.
2289
This method raises a ``ReloadError`` if the ConfigObj doesn't have
2290
a filename attribute pointing to a file.
2292
if not isinstance(self.filename, basestring):
2295
filename = self.filename
2296
current_options = {}
2297
for entry in OPTION_DEFAULTS:
2298
if entry == 'configspec':
2300
current_options[entry] = getattr(self, entry)
2302
configspec = self._original_configspec
2303
current_options['configspec'] = configspec
2306
self._initialise(current_options)
2307
self._load(filename, configspec)
2311
class SimpleVal(object):
2314
Can be used to check that all members expected are present.
2316
To use it, provide a configspec with all your members in (the value given
2317
will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2318
method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2319
members are present, or a dictionary with True/False meaning
2320
present/missing. (Whole missing sections will be replaced with ``False``)
2324
self.baseErrorClass = ConfigObjError
2326
def check(self, check, member, missing=False):
2327
"""A dummy check method, always returns the value unchanged."""
2329
raise self.baseErrorClass()
2333
# Check / processing functions for options
2334
def flatten_errors(cfg, res, levels=None, results=None):
2336
An example function that will turn a nested dictionary of results
2337
(as returned by ``ConfigObj.validate``) into a flat list.
2339
``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2340
dictionary returned by ``validate``.
2342
(This is a recursive function, so you shouldn't use the ``levels`` or
2343
``results`` arguments - they are used by the function.)
2345
Returns a list of keys that failed. Each member of the list is a tuple :
2349
([list of sections...], key, result)
2351
If ``validate`` was called with ``preserve_errors=False`` (the default)
2352
then ``result`` will always be ``False``.
2354
*list of sections* is a flattened list of sections that the key was found
2357
If the section was missing (or a section was expected and a scalar provided
2358
- or vice-versa) then key will be ``None``.
2360
If the value (or section) was missing then ``result`` will be ``False``.
2362
If ``validate`` was called with ``preserve_errors=True`` and a value
2363
was present, but failed the check, then ``result`` will be the exception
2364
object returned. You can use this as a string that describes the failure.
2366
For example *The value "3" is of the wrong type*.
2369
>>> vtor = validate.Validator()
2375
... another_option = Probably
2377
... another_option = True
2384
... option1 = boolean()
2385
... option2 = boolean()
2386
... option3 = boolean(default=Bad_value)
2388
... option1 = boolean()
2389
... option2 = boolean()
2390
... option3 = boolean(default=Bad_value)
2392
... another_option = boolean()
2394
... another_option = boolean()
2397
... value2 = integer
2398
... value3 = integer(0, 10)
2399
... [[[section3b-sub]]]
2402
... another_option = boolean()
2404
>>> cs = my_cfg.split('\\n')
2405
>>> ini = my_ini.split('\\n')
2406
>>> cfg = ConfigObj(ini, configspec=cs)
2407
>>> res = cfg.validate(vtor, preserve_errors=True)
2409
>>> for entry in flatten_errors(cfg, res):
2410
... section_list, key, error = entry
2411
... section_list.insert(0, '[root]')
2412
... if key is not None:
2413
... section_list.append(key)
2415
... section_list.append('[missing]')
2416
... section_string = ', '.join(section_list)
2417
... errors.append((section_string, ' = ', error))
2419
>>> for entry in errors:
2420
... print entry[0], entry[1], (entry[2] or 0)
2422
[root], option3 = the value "Bad_value" is of the wrong type.
2423
[root], section1, option2 = 0
2424
[root], section1, option3 = the value "Bad_value" is of the wrong type.
2425
[root], section2, another_option = the value "Probably" is of the wrong type.
2426
[root], section3, section3b, section3b-sub, [missing] = 0
2427
[root], section3, section3b, value2 = the value "a" is of the wrong type.
2428
[root], section3, section3b, value3 = the value "11" is too big.
2429
[root], section4, [missing] = 0
2437
if res is False or isinstance(res, Exception):
2438
results.append((levels[:], None, res))
2442
for (key, val) in res.items():
2445
if isinstance(cfg.get(key), dict):
2448
flatten_errors(cfg[key], val, levels, results)
2450
results.append((levels[:], key, val))
2459
"""*A programming language is a medium of expression.* - Paul Graham"""