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)
386 val = current_section.get('DEFAULT', {}).get(key)
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 return [_check(entry) for entry in val]
580 def __setitem__(self, key, value, unrepr=False):
582 Correctly set a value.
584 Making dictionary values Section instances.
585 (We have to special case 'Section' instances - which are also dicts)
587 Keys must be strings.
588 Values need only be strings (or lists of strings) if
589 ``main.stringify`` is set.
591 ``unrepr`` must be set when setting a value to a dictionary, without
592 creating a new sub-section.
594 if not isinstance(key, basestring):
595 raise ValueError('The key "%s" is not a string.' % key)
598 if key not in self.comments:
599 self.comments[key] = []
600 self.inline_comments[key] = ''
601 # remove the entry from defaults
602 if key in self.defaults:
603 self.defaults.remove(key)
605 if isinstance(value, Section):
607 self.sections.append(key)
608 dict.__setitem__(self, key, value)
609 elif isinstance(value, dict) and not unrepr:
610 # First create the new depth level,
611 # then create the section
613 self.sections.append(key)
614 new_depth = self.depth + 1
626 self.scalars.append(key)
627 if not self.main.stringify:
628 if isinstance(value, basestring):
630 elif isinstance(value, (list, tuple)):
632 if not isinstance(entry, basestring):
633 raise TypeError('Value is not a string "%s".' % entry)
635 raise TypeError('Value is not a string "%s".' % value)
636 dict.__setitem__(self, key, value)
639 def __delitem__(self, key):
640 """Remove items from the sequence when deleting."""
641 dict. __delitem__(self, key)
642 if key in self.scalars:
643 self.scalars.remove(key)
645 self.sections.remove(key)
646 del self.comments[key]
647 del self.inline_comments[key]
650 def get(self, key, default=None):
651 """A version of ``get`` that doesn't bypass string interpolation."""
658 def update(self, indict):
660 A version of update that uses our ``__setitem__``.
663 self[entry] = indict[entry]
666 def pop(self, key, *args):
668 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
669 If key is not found, d is returned if given, otherwise KeyError is raised'
671 val = dict.pop(self, key, *args)
672 if key in self.scalars:
673 del self.comments[key]
674 del self.inline_comments[key]
675 self.scalars.remove(key)
676 elif key in self.sections:
677 del self.comments[key]
678 del self.inline_comments[key]
679 self.sections.remove(key)
680 if self.main.interpolation and isinstance(val, basestring):
681 return self._interpolate(key, val)
686 """Pops the first (key,val)"""
687 sequence = (self.scalars + self.sections)
689 raise KeyError(": 'popitem(): dictionary is empty'")
698 A version of clear that also affects scalars/sections
699 Also clears comments and configspec.
701 Leaves other attributes alone :
702 depth/main/parent are not affected
708 self.inline_comments = {}
709 self.configspec = None
711 self.extra_values = []
714 def setdefault(self, key, default=None):
715 """A version of setdefault that sets sequence if appropriate."""
724 """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
725 return zip((self.scalars + self.sections), self.values())
729 """D.keys() -> list of D's keys"""
730 return (self.scalars + self.sections)
734 """D.values() -> list of D's values"""
735 return [self[key] for key in (self.scalars + self.sections)]
739 """D.iteritems() -> an iterator over the (key, value) items of D"""
740 return iter(self.items())
744 """D.iterkeys() -> an iterator over the keys of D"""
745 return iter((self.scalars + self.sections))
750 def itervalues(self):
751 """D.itervalues() -> an iterator over the values of D"""
752 return iter(self.values())
756 """x.__repr__() <==> repr(x)"""
757 return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
758 for key in (self.scalars + self.sections)])
761 __str__.__doc__ = "x.__str__() <==> str(x)"
764 # Extra methods - not in a normal dictionary
768 Return a deepcopy of self as a dictionary.
770 All members that are ``Section`` instances are recursively turned to
771 ordinary dictionaries - by calling their ``dict`` method.
781 this_entry = self[entry]
782 if isinstance(this_entry, Section):
783 this_entry = this_entry.dict()
784 elif isinstance(this_entry, list):
785 # create a copy rather than a reference
786 this_entry = list(this_entry)
787 elif isinstance(this_entry, tuple):
788 # create a copy rather than a reference
789 this_entry = tuple(this_entry)
790 newdict[entry] = this_entry
794 def merge(self, indict):
796 A recursive update - useful for merging config files.
798 >>> a = '''[section1]
801 ... more_options = False
802 ... # end of file'''.splitlines()
803 >>> b = '''# File is user.ini
806 ... # end of file'''.splitlines()
807 >>> c1 = ConfigObj(b)
808 >>> c2 = ConfigObj(a)
811 ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
813 for key, val in indict.items():
814 if (key in self and isinstance(self[key], dict) and
815 isinstance(val, dict)):
821 def rename(self, oldkey, newkey):
823 Change a keyname to another, without changing position in sequence.
825 Implemented so that transformations can be made on keys,
826 as well as on values. (used by encode and decode)
828 Also renames comments.
830 if oldkey in self.scalars:
831 the_list = self.scalars
832 elif oldkey in self.sections:
833 the_list = self.sections
835 raise KeyError('Key "%s" not found.' % oldkey)
836 pos = the_list.index(oldkey)
839 dict.__delitem__(self, oldkey)
840 dict.__setitem__(self, newkey, val)
841 the_list.remove(oldkey)
842 the_list.insert(pos, newkey)
843 comm = self.comments[oldkey]
844 inline_comment = self.inline_comments[oldkey]
845 del self.comments[oldkey]
846 del self.inline_comments[oldkey]
847 self.comments[newkey] = comm
848 self.inline_comments[newkey] = inline_comment
851 def walk(self, function, raise_errors=True,
852 call_on_sections=False, **keywargs):
854 Walk every member and call a function on the keyword and value.
856 Return a dictionary of the return values
858 If the function raises an exception, raise the errror
859 unless ``raise_errors=False``, in which case set the return value to
862 Any unrecognised keyword arguments you pass to walk, will be pased on
863 to the function you pass in.
865 Note: if ``call_on_sections`` is ``True`` then - on encountering a
866 subsection, *first* the function is called for the *whole* subsection,
867 and then recurses into it's members. This means your function must be
868 able to handle strings, dictionaries and lists. This allows you
869 to change the key of subsections as well as for ordinary members. The
870 return value when called on the whole subsection has to be discarded.
872 See the encode and decode methods for examples, including functions.
874 .. admonition:: caution
876 You can use ``walk`` to transform the names of members of a section
877 but you mustn't add or delete members.
879 >>> config = '''[XXXXsection]
880 ... XXXXkey = XXXXvalue'''.splitlines()
881 >>> cfg = ConfigObj(config)
883 ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
884 >>> def transform(section, key):
885 ... val = section[key]
886 ... newkey = key.replace('XXXX', 'CLIENT1')
887 ... section.rename(key, newkey)
888 ... if isinstance(val, (tuple, list, dict)):
891 ... val = val.replace('XXXX', 'CLIENT1')
892 ... section[newkey] = val
893 >>> cfg.walk(transform, call_on_sections=True)
894 {'CLIENT1section': {'CLIENT1key': None}}
896 ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
900 for i in range(len(self.scalars)):
901 entry = self.scalars[i]
903 val = function(self, entry, **keywargs)
904 # bound again in case name has changed
905 entry = self.scalars[i]
911 entry = self.scalars[i]
914 for i in range(len(self.sections)):
915 entry = self.sections[i]
918 function(self, entry, **keywargs)
923 entry = self.sections[i]
925 # bound again in case name has changed
926 entry = self.sections[i]
927 # previous result is discarded
928 out[entry] = self[entry].walk(
930 raise_errors=raise_errors,
931 call_on_sections=call_on_sections,
936 def as_bool(self, key):
938 Accepts a key as input. The corresponding value must be a string or
939 the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
940 retain compatibility with Python 2.2.
942 If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
945 If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
948 ``as_bool`` is not case sensitive.
950 Any other input will raise a ``ValueError``.
955 Traceback (most recent call last):
956 ValueError: Value "fish" is neither True nor False
971 if not isinstance(val, basestring):
972 # TODO: Why do we raise a KeyError here?
975 return self.main._bools[val.lower()]
977 raise ValueError('Value "%s" is neither True nor False' % val)
980 def as_int(self, key):
982 A convenience method which coerces the specified value to an integer.
984 If the value is an invalid literal for ``int``, a ``ValueError`` will
990 Traceback (most recent call last):
991 ValueError: invalid literal for int() with base 10: 'fish'
997 Traceback (most recent call last):
998 ValueError: invalid literal for int() with base 10: '3.2'
1000 return int(self[key])
1003 def as_float(self, key):
1005 A convenience method which coerces the specified value to a float.
1007 If the value is an invalid literal for ``float``, a ``ValueError`` will
1013 Traceback (most recent call last):
1014 ValueError: invalid literal for float(): fish
1022 return float(self[key])
1025 def as_list(self, key):
1027 A convenience method which fetches the specified value, guaranteeing
1042 if isinstance(result, (tuple, list)):
1047 def restore_default(self, key):
1049 Restore (and return) default value for the specified key.
1051 This method will only work for a ConfigObj that was created
1052 with a configspec and has been validated.
1054 If there is no default value for this key, ``KeyError`` is raised.
1056 default = self.default_values[key]
1057 dict.__setitem__(self, key, default)
1058 if key not in self.defaults:
1059 self.defaults.append(key)
1063 def restore_defaults(self):
1065 Recursively restore default values to all members
1068 This method will only work for a ConfigObj that was created
1069 with a configspec and has been validated.
1071 It doesn't delete or modify entries without default values.
1073 for key in self.default_values:
1074 self.restore_default(key)
1076 for section in self.sections:
1077 self[section].restore_defaults()
1080 class ConfigObj(Section):
1081 """An object to read, create, and write config files."""
1083 _keyword = re.compile(r'''^ # line start
1086 (?:".*?")| # double quotes
1087 (?:'.*?')| # single quotes
1088 (?:[^'"=].*?) # no quotes
1091 (.*) # value (including list values and comments)
1096 _sectionmarker = re.compile(r'''^
1097 (\s*) # 1: indentation
1098 ((?:\[\s*)+) # 2: section marker open
1099 ( # 3: section name open
1100 (?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1101 (?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1102 (?:[^'"\s].*?) # at least one non-space unquoted
1103 ) # section name close
1104 ((?:\s*\])+) # 4: section marker close
1105 \s*(\#.*)? # 5: optional comment
1109 # this regexp pulls list values out as a single string
1110 # or single values and comments
1111 # FIXME: this regex adds a '' to the end of comma terminated lists
1112 # workaround in ``_handle_value``
1113 _valueexp = re.compile(r'''^
1119 (?:".*?")| # double quotes
1120 (?:'.*?')| # single quotes
1121 (?:[^'",\#][^,\#]*?) # unquoted
1124 )* # match all list items ending in a comma (if any)
1127 (?:".*?")| # double quotes
1128 (?:'.*?')| # single quotes
1129 (?:[^'",\#\s][^,]*?)| # unquoted
1130 (?:(?<!,)) # Empty value
1131 )? # last item in a list - or string value
1133 (,) # alternatively a single comma - empty list
1135 \s*(\#.*)? # optional comment
1139 # use findall to get the members of a list value
1140 _listvalueexp = re.compile(r'''
1142 (?:".*?")| # double quotes
1143 (?:'.*?')| # single quotes
1144 (?:[^'",\#]?.*?) # unquoted
1150 # this regexp is used for the value
1151 # when lists are switched off
1152 _nolistvalue = re.compile(r'''^
1154 (?:".*?")| # double quotes
1155 (?:'.*?')| # single quotes
1156 (?:[^'"\#].*?)| # unquoted
1159 \s*(\#.*)? # optional comment
1163 # regexes for finding triple quoted values on one line
1164 _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1165 _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1166 _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1167 _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1170 "'''": (_single_line_single, _multi_line_single),
1171 '"""': (_single_line_double, _multi_line_double),
1174 # Used by the ``istrue`` Section method
1176 'yes': True, 'no': False,
1177 'on': True, 'off': False,
1178 '1': True, '0': False,
1179 'true': True, 'false': False,
1183 def __init__(self, infile=None, options=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 Parse a config file or create a config file object.
1191 ``ConfigObj(infile=None, configspec=None, encoding=None,
1192 interpolation=True, raise_errors=False, list_values=True,
1193 create_empty=False, file_error=False, stringify=True,
1194 indent_type=None, default_encoding=None, unrepr=False,
1195 write_empty_values=False, _inspec=False)``
1197 self._inspec = _inspec
1198 # init the superclass
1199 Section.__init__(self, self, 0, self)
1201 infile = infile or []
1203 _options = {'configspec': configspec,
1204 'encoding': encoding, 'interpolation': interpolation,
1205 'raise_errors': raise_errors, 'list_values': list_values,
1206 'create_empty': create_empty, 'file_error': file_error,
1207 'stringify': stringify, 'indent_type': indent_type,
1208 'default_encoding': default_encoding, 'unrepr': unrepr,
1209 'write_empty_values': write_empty_values}
1215 warnings.warn('Passing in an options dictionary to ConfigObj() is '
1216 'deprecated. Use **options instead.',
1217 DeprecationWarning, stacklevel=2)
1219 # TODO: check the values too.
1220 for entry in options:
1221 if entry not in OPTION_DEFAULTS:
1222 raise TypeError('Unrecognised option "%s".' % entry)
1223 for entry, value in OPTION_DEFAULTS.items():
1224 if entry not in options:
1225 options[entry] = value
1226 keyword_value = _options[entry]
1227 if value != keyword_value:
1228 options[entry] = keyword_value
1230 # XXXX this ignores an explicit list_values = True in combination
1231 # with _inspec. The user should *never* do that anyway, but still...
1233 options['list_values'] = False
1235 self._initialise(options)
1236 configspec = options['configspec']
1237 self._original_configspec = configspec
1238 self._load(infile, configspec)
1241 def _load(self, infile, configspec):
1242 if isinstance(infile, basestring):
1243 self.filename = infile
1244 if os.path.isfile(infile):
1245 h = open(infile, 'rb')
1246 infile = h.read() or []
1248 elif self.file_error:
1249 # raise an error if the file doesn't exist
1250 raise IOError('Config file not found: "%s".' % self.filename)
1252 # file doesn't already exist
1253 if self.create_empty:
1254 # this is a good test that the filename specified
1255 # isn't impossible - like on a non-existent device
1256 h = open(infile, 'w')
1261 elif isinstance(infile, (list, tuple)):
1262 infile = list(infile)
1264 elif isinstance(infile, dict):
1266 # the Section class handles creating subsections
1267 if isinstance(infile, ConfigObj):
1268 # get a copy of our ConfigObj
1269 def set_section(in_section, this_section):
1270 for entry in in_section.scalars:
1271 this_section[entry] = in_section[entry]
1272 for section in in_section.sections:
1273 this_section[section] = {}
1274 set_section(in_section[section], this_section[section])
1275 set_section(infile, self)
1278 for entry in infile:
1279 self[entry] = infile[entry]
1282 if configspec is not None:
1283 self._handle_configspec(configspec)
1285 self.configspec = None
1288 elif getattr(infile, 'read', MISSING) is not MISSING:
1289 # This supports file like objects
1290 infile = infile.read() or []
1291 # needs splitting into lines - but needs doing *after* decoding
1292 # in case it's not an 8 bit encoding
1294 raise TypeError('infile must be a filename, file like object, or list of lines.')
1297 # don't do it for the empty ConfigObj
1298 infile = self._handle_bom(infile)
1299 # infile is now *always* a list
1301 # Set the newlines attribute (first line ending it finds)
1302 # and strip trailing '\n' or '\r' from lines
1304 if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1306 for end in ('\r\n', '\n', '\r'):
1307 if line.endswith(end):
1312 infile = [line.rstrip('\r\n') for line in infile]
1315 # if we had any errors, now is the time to raise them
1317 info = "at line %s." % self._errors[0].line_number
1318 if len(self._errors) > 1:
1319 msg = "Parsing failed with several errors.\nFirst error %s" % info
1320 error = ConfigObjError(msg)
1322 error = self._errors[0]
1323 # set the errors attribute; it's a list of tuples:
1324 # (error_type, message, line_number)
1325 error.errors = self._errors
1326 # set the config attribute
1329 # delete private attributes
1332 if configspec is None:
1333 self.configspec = None
1335 self._handle_configspec(configspec)
1338 def _initialise(self, options=None):
1340 options = OPTION_DEFAULTS
1342 # initialise a few variables
1343 self.filename = None
1345 self.raise_errors = options['raise_errors']
1346 self.interpolation = options['interpolation']
1347 self.list_values = options['list_values']
1348 self.create_empty = options['create_empty']
1349 self.file_error = options['file_error']
1350 self.stringify = options['stringify']
1351 self.indent_type = options['indent_type']
1352 self.encoding = options['encoding']
1353 self.default_encoding = options['default_encoding']
1355 self.newlines = None
1356 self.write_empty_values = options['write_empty_values']
1357 self.unrepr = options['unrepr']
1359 self.initial_comment = []
1360 self.final_comment = []
1361 self.configspec = None
1364 self.list_values = False
1366 # Clear section attributes as well
1367 Section._initialise(self)
1371 return ('ConfigObj({%s})' %
1372 ', '.join([('%s: %s' % (repr(key), repr(self[key])))
1373 for key in (self.scalars + self.sections)]))
1376 def _handle_bom(self, infile):
1378 Handle any BOM, and decode if necessary.
1380 If an encoding is specified, that *must* be used - but the BOM should
1381 still be removed (and the BOM attribute set).
1383 (If the encoding is wrongly specified, then a BOM for an alternative
1384 encoding won't be discovered or removed.)
1386 If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1387 removed. The BOM attribute will be set. UTF16 will be decoded to
1390 NOTE: This method must not be called with an empty ``infile``.
1392 Specifying the *wrong* encoding is likely to cause a
1393 ``UnicodeDecodeError``.
1395 ``infile`` must always be returned as a list of lines, but may be
1396 passed in as a single string.
1398 if ((self.encoding is not None) and
1399 (self.encoding.lower() not in BOM_LIST)):
1400 # No need to check for a BOM
1401 # the encoding specified doesn't have one
1403 return self._decode(infile, self.encoding)
1405 if isinstance(infile, (list, tuple)):
1409 if self.encoding is not None:
1410 # encoding explicitly supplied
1411 # And it could have an associated BOM
1412 # TODO: if encoding is just UTF16 - we ought to check for both
1413 # TODO: big endian and little endian versions.
1414 enc = BOM_LIST[self.encoding.lower()]
1416 # For UTF16 we try big endian and little endian
1417 for BOM, (encoding, final_encoding) in BOMS.items():
1418 if not final_encoding:
1421 if infile.startswith(BOM):
1424 # Don't need to remove BOM
1425 return self._decode(infile, encoding)
1427 # If we get this far, will *probably* raise a DecodeError
1428 # As it doesn't appear to start with a BOM
1429 return self._decode(infile, self.encoding)
1433 if not line.startswith(BOM):
1434 return self._decode(infile, self.encoding)
1436 newline = line[len(BOM):]
1439 if isinstance(infile, (list, tuple)):
1444 return self._decode(infile, self.encoding)
1446 # No encoding specified - so we need to check for UTF8/UTF16
1447 for BOM, (encoding, final_encoding) in BOMS.items():
1448 if not line.startswith(BOM):
1452 self.encoding = final_encoding
1453 if not final_encoding:
1457 newline = line[len(BOM):]
1458 if isinstance(infile, (list, tuple)):
1462 # UTF8 - don't decode
1463 if isinstance(infile, basestring):
1464 return infile.splitlines(True)
1467 # UTF16 - have to decode
1468 return self._decode(infile, encoding)
1470 # No BOM discovered and no encoding specified, just return
1471 if isinstance(infile, basestring):
1472 # infile read from a file will be a single string
1473 return infile.splitlines(True)
1477 def _a_to_u(self, aString):
1478 """Decode ASCII strings to unicode if a self.encoding is specified."""
1480 return aString.decode('ascii')
1485 def _decode(self, infile, encoding):
1487 Decode infile to unicode. Using the specified encoding.
1489 if is a string, it also needs converting to a list.
1491 if isinstance(infile, basestring):
1493 # NOTE: Could raise a ``UnicodeDecodeError``
1494 return infile.decode(encoding).splitlines(True)
1495 for i, line in enumerate(infile):
1496 if not isinstance(line, unicode):
1497 # NOTE: The isinstance test here handles mixed lists of unicode/string
1498 # NOTE: But the decode will break on any non-string values
1499 # NOTE: Or could raise a ``UnicodeDecodeError``
1500 infile[i] = line.decode(encoding)
1504 def _decode_element(self, line):
1505 """Decode element to unicode if necessary."""
1506 if not self.encoding:
1508 if isinstance(line, str) and self.default_encoding:
1509 return line.decode(self.default_encoding)
1513 def _str(self, value):
1515 Used by ``stringify`` within validate, to turn non-string values
1518 if not isinstance(value, basestring):
1524 def _parse(self, infile):
1525 """Actually parse the config file."""
1526 temp_list_values = self.list_values
1528 self.list_values = False
1533 maxline = len(infile) - 1
1535 reset_comment = False
1537 while cur_index < maxline:
1541 line = infile[cur_index]
1542 sline = line.strip()
1543 # do we have anything on the line ?
1544 if not sline or sline.startswith('#'):
1545 reset_comment = False
1546 comment_list.append(line)
1550 # preserve initial comment
1551 self.initial_comment = comment_list
1555 reset_comment = True
1556 # first we check if it's a section marker
1557 mat = self._sectionmarker.match(line)
1560 (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1561 if indent and (self.indent_type is None):
1562 self.indent_type = indent
1563 cur_depth = sect_open.count('[')
1564 if cur_depth != sect_close.count(']'):
1565 self._handle_error("Cannot compute the section depth at line %s.",
1566 NestingError, infile, cur_index)
1569 if cur_depth < this_section.depth:
1570 # the new section is dropping back to a previous level
1572 parent = self._match_depth(this_section,
1575 self._handle_error("Cannot compute nesting level at line %s.",
1576 NestingError, infile, cur_index)
1578 elif cur_depth == this_section.depth:
1579 # the new section is a sibling of the current section
1580 parent = this_section.parent
1581 elif cur_depth == this_section.depth + 1:
1582 # the new section is a child the current section
1583 parent = this_section
1585 self._handle_error("Section too nested at line %s.",
1586 NestingError, infile, cur_index)
1588 sect_name = self._unquote(sect_name)
1589 if sect_name in parent:
1590 self._handle_error('Duplicate section name at line %s.',
1591 DuplicateError, infile, cur_index)
1594 # create the new section
1595 this_section = Section(
1600 parent[sect_name] = this_section
1601 parent.inline_comments[sect_name] = comment
1602 parent.comments[sect_name] = comment_list
1605 # it's not a section marker,
1606 # so it should be a valid ``key = value`` line
1607 mat = self._keyword.match(line)
1609 # it neither matched as a keyword
1610 # or a section marker
1612 'Invalid line at line "%s".',
1613 ParseError, infile, cur_index)
1615 # is a keyword value
1616 # value will include any inline comment
1617 (indent, key, value) = mat.groups()
1618 if indent and (self.indent_type is None):
1619 self.indent_type = indent
1620 # check for a multiline value
1621 if value[:3] in ['"""', "'''"]:
1623 value, comment, cur_index = self._multiline(
1624 value, infile, cur_index, maxline)
1627 'Parse error in value at line %s.',
1628 ParseError, infile, cur_index)
1634 value = unrepr(value)
1635 except Exception, e:
1636 if type(e) == UnknownType:
1637 msg = 'Unknown name or type in value at line %s.'
1639 msg = 'Parse error in value at line %s.'
1640 self._handle_error(msg, UnreprError, infile,
1647 value = unrepr(value)
1648 except Exception, e:
1649 if isinstance(e, UnknownType):
1650 msg = 'Unknown name or type in value at line %s.'
1652 msg = 'Parse error in value at line %s.'
1653 self._handle_error(msg, UnreprError, infile,
1657 # extract comment and lists
1659 (value, comment) = self._handle_value(value)
1662 'Parse error in value at line %s.',
1663 ParseError, infile, cur_index)
1666 key = self._unquote(key)
1667 if key in this_section:
1669 'Duplicate keyword name at line %s.',
1670 DuplicateError, infile, cur_index)
1673 # we set unrepr because if we have got this far we will never
1674 # be creating a new section
1675 this_section.__setitem__(key, value, unrepr=True)
1676 this_section.inline_comments[key] = comment
1677 this_section.comments[key] = comment_list
1680 if self.indent_type is None:
1681 # no indentation used, set the type accordingly
1682 self.indent_type = ''
1684 # preserve the final comment
1685 if not self and not self.initial_comment:
1686 self.initial_comment = comment_list
1687 elif not reset_comment:
1688 self.final_comment = comment_list
1689 self.list_values = temp_list_values
1692 def _match_depth(self, sect, depth):
1694 Given a section and a depth level, walk back through the sections
1695 parents to see if the depth level matches a previous section.
1697 Return a reference to the right section,
1698 or raise a SyntaxError.
1700 while depth < sect.depth:
1701 if sect is sect.parent:
1702 # we've reached the top level already
1705 if sect.depth == depth:
1707 # shouldn't get here
1711 def _handle_error(self, text, ErrorClass, infile, cur_index):
1713 Handle an error according to the error settings.
1715 Either raise the error or store it.
1716 The error will have occured at ``cur_index``
1718 line = infile[cur_index]
1720 message = text % cur_index
1721 error = ErrorClass(message, cur_index, line)
1722 if self.raise_errors:
1723 # raise the error - parsing stops here
1726 # reraise when parsing has finished
1727 self._errors.append(error)
1730 def _unquote(self, value):
1731 """Return an unquoted version of a value"""
1733 # should only happen during parsing of lists
1735 if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1740 def _quote(self, value, multiline=True):
1742 Return a safely quoted version of a value.
1744 Raise a ConfigObjError if the value cannot be safely quoted.
1745 If multiline is ``True`` (default) then use triple quotes
1748 * Don't quote values that don't need it.
1749 * Recursively quote members of a list and return a comma joined list.
1750 * Multiline is ``False`` for lists.
1751 * Obey list syntax for empty and single member lists.
1753 If ``list_values=False`` then the value is only quoted if it contains
1754 a ``\\n`` (is multiline) or '#'.
1756 If ``write_empty_values`` is set, and the value is an empty string, it
1759 if multiline and self.write_empty_values and value == '':
1760 # Only if multiline is set, so that it is used for values not
1761 # keys, and not values that are part of a list
1764 if multiline and isinstance(value, (list, tuple)):
1767 elif len(value) == 1:
1768 return self._quote(value[0], multiline=False) + ','
1769 return ', '.join([self._quote(val, multiline=False)
1771 if not isinstance(value, basestring):
1775 raise TypeError('Value "%s" is not a string.' % value)
1780 no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1781 need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1782 hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1783 check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1785 if check_for_single:
1786 if not self.list_values:
1787 # we don't quote if ``list_values=False``
1789 # for normal values either single or double quotes will do
1791 # will only happen if multiline is off - e.g. '\n' in key
1792 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1793 elif ((value[0] not in wspace_plus) and
1794 (value[-1] not in wspace_plus) and
1795 (',' not in value)):
1798 quot = self._get_single_quote(value)
1800 # if value has '\n' or "'" *and* '"', it will need triple quotes
1801 quot = self._get_triple_quote(value)
1803 if quot == noquot and '#' in value and self.list_values:
1804 quot = self._get_single_quote(value)
1809 def _get_single_quote(self, value):
1810 if ("'" in value) and ('"' in value):
1811 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1819 def _get_triple_quote(self, value):
1820 if (value.find('"""') != -1) and (value.find("'''") != -1):
1821 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1822 if value.find('"""') == -1:
1829 def _handle_value(self, value):
1831 Given a value string, unquote, remove comment,
1832 handle lists. (including empty and single member lists)
1835 # Parsing a configspec so don't handle comments
1837 # do we look for lists in values ?
1838 if not self.list_values:
1839 mat = self._nolistvalue.match(value)
1842 # NOTE: we don't unquote here
1845 mat = self._valueexp.match(value)
1847 # the value is badly constructed, probably badly quoted,
1848 # or an invalid list
1850 (list_values, single, empty_list, comment) = mat.groups()
1851 if (list_values == '') and (single is None):
1852 # change this if you want to accept empty values
1854 # NOTE: note there is no error handling from here if the regex
1855 # is wrong: then incorrect values will slip through
1856 if empty_list is not None:
1857 # the single comma - meaning an empty list
1858 return ([], comment)
1859 if single is not None:
1860 # handle empty values
1861 if list_values and not single:
1862 # FIXME: the '' is a workaround because our regex now matches
1863 # '' at the end of a list if it has a trailing comma
1866 single = single or '""'
1867 single = self._unquote(single)
1868 if list_values == '':
1870 return (single, comment)
1871 the_list = self._listvalueexp.findall(list_values)
1872 the_list = [self._unquote(val) for val in the_list]
1873 if single is not None:
1874 the_list += [single]
1875 return (the_list, comment)
1878 def _multiline(self, value, infile, cur_index, maxline):
1879 """Extract the value, where we are in a multiline situation."""
1881 newvalue = value[3:]
1882 single_line = self._triple_quote[quot][0]
1883 multi_line = self._triple_quote[quot][1]
1884 mat = single_line.match(value)
1886 retval = list(mat.groups())
1887 retval.append(cur_index)
1889 elif newvalue.find(quot) != -1:
1890 # somehow the triple quote is missing
1893 while cur_index < maxline:
1896 line = infile[cur_index]
1897 if line.find(quot) == -1:
1900 # end of multiline, process it
1903 # we've got to the end of the config, oops...
1905 mat = multi_line.match(line)
1907 # a badly formed line
1909 (value, comment) = mat.groups()
1910 return (newvalue + value, comment, cur_index)
1913 def _handle_configspec(self, configspec):
1914 """Parse the configspec."""
1915 # FIXME: Should we check that the configspec was created with the
1916 # correct settings ? (i.e. ``list_values=False``)
1917 if not isinstance(configspec, ConfigObj):
1919 configspec = ConfigObj(configspec,
1923 except ConfigObjError, e:
1924 # FIXME: Should these errors have a reference
1925 # to the already parsed ConfigObj ?
1926 raise ConfigspecError('Parsing configspec failed: %s' % e)
1928 raise IOError('Reading configspec failed: %s' % e)
1930 self.configspec = configspec
1934 def _set_configspec(self, section, copy):
1936 Called by validate. Handles setting the configspec on subsections
1937 including sections to be validated by __many__
1939 configspec = section.configspec
1940 many = configspec.get('__many__')
1941 if isinstance(many, dict):
1942 for entry in section.sections:
1943 if entry not in configspec:
1944 section[entry].configspec = many
1946 for entry in configspec.sections:
1947 if entry == '__many__':
1949 if entry not in section:
1951 section[entry]._created = True
1954 section.comments[entry] = configspec.comments.get(entry, [])
1955 section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1957 # Could be a scalar when we expect a section
1958 if isinstance(section[entry], Section):
1959 section[entry].configspec = configspec[entry]
1962 def _write_line(self, indent_string, entry, this_entry, comment):
1963 """Write an individual line, for the write method"""
1964 # NOTE: the calls to self._quote here handles non-StringType values.
1966 val = self._decode_element(self._quote(this_entry))
1968 val = repr(this_entry)
1969 return '%s%s%s%s%s' % (indent_string,
1970 self._decode_element(self._quote(entry, multiline=False)),
1971 self._a_to_u(' = '),
1973 self._decode_element(comment))
1976 def _write_marker(self, indent_string, depth, entry, comment):
1977 """Write a section marker line"""
1978 return '%s%s%s%s%s' % (indent_string,
1979 self._a_to_u('[' * depth),
1980 self._quote(self._decode_element(entry), multiline=False),
1981 self._a_to_u(']' * depth),
1982 self._decode_element(comment))
1985 def _handle_comment(self, comment):
1986 """Deal with a comment."""
1989 start = self.indent_type
1990 if not comment.startswith('#'):
1991 start += self._a_to_u(' # ')
1992 return (start + comment)
1997 def write(self, outfile=None, section=None):
1999 Write the current ConfigObj as a file
2001 tekNico: FIXME: use StringIO instead of real files
2003 >>> filename = a.filename
2004 >>> a.filename = 'test.ini'
2006 >>> a.filename = filename
2007 >>> a == ConfigObj('test.ini', raise_errors=True)
2010 if self.indent_type is None:
2011 # this can be true if initialised from a dictionary
2012 self.indent_type = DEFAULT_INDENT_TYPE
2015 cs = self._a_to_u('#')
2016 csp = self._a_to_u('# ')
2018 int_val = self.interpolation
2019 self.interpolation = False
2021 for line in self.initial_comment:
2022 line = self._decode_element(line)
2023 stripped_line = line.strip()
2024 if stripped_line and not stripped_line.startswith(cs):
2028 indent_string = self.indent_type * section.depth
2029 for entry in (section.scalars + section.sections):
2030 if entry in section.defaults:
2031 # don't write out default values
2033 for comment_line in section.comments[entry]:
2034 comment_line = self._decode_element(comment_line.lstrip())
2035 if comment_line and not comment_line.startswith(cs):
2036 comment_line = csp + comment_line
2037 out.append(indent_string + comment_line)
2038 this_entry = section[entry]
2039 comment = self._handle_comment(section.inline_comments[entry])
2041 if isinstance(this_entry, dict):
2043 out.append(self._write_marker(
2048 out.extend(self.write(section=this_entry))
2050 out.append(self._write_line(
2057 for line in self.final_comment:
2058 line = self._decode_element(line)
2059 stripped_line = line.strip()
2060 if stripped_line and not stripped_line.startswith(cs):
2063 self.interpolation = int_val
2065 if section is not self:
2068 if (self.filename is None) and (outfile is None):
2069 # output a list of lines
2070 # might need to encode
2071 # NOTE: This will *screw* UTF16, each line will start with the BOM
2073 out = [l.encode(self.encoding) for l in out]
2074 if (self.BOM and ((self.encoding is None) or
2075 (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2079 out[0] = BOM_UTF8 + out[0]
2082 # Turn the list to a string, joined with correct newlines
2083 newline = self.newlines or os.linesep
2084 output = self._a_to_u(newline).join(out)
2086 output = output.encode(self.encoding)
2087 if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2089 output = BOM_UTF8 + output
2091 if not output.endswith(newline):
2093 if outfile is not None:
2094 outfile.write(output)
2096 h = open(self.filename, 'wb')
2101 def validate(self, validator, preserve_errors=False, copy=False,
2104 Test the ConfigObj against a configspec.
2106 It uses the ``validator`` object from *validate.py*.
2108 To run ``validate`` on the current ConfigObj, call: ::
2110 test = config.validate(validator)
2112 (Normally having previously passed in the configspec when the ConfigObj
2113 was created - you can dynamically assign a dictionary of checks to the
2114 ``configspec`` attribute of a section though).
2116 It returns ``True`` if everything passes, or a dictionary of
2117 pass/fails (True/False). If every member of a subsection passes, it
2118 will just have the value ``True``. (It also returns ``False`` if all
2121 In addition, it converts the values from strings to their native
2122 types if their checks pass (and ``stringify`` is set).
2124 If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2125 of a marking a fail with a ``False``, it will preserve the actual
2126 exception object. This can contain info about the reason for failure.
2127 For example the ``VdtValueTooSmallError`` indicates that the value
2128 supplied was too small. If a value (or section) is missing it will
2129 still be marked as ``False``.
2131 You must have the validate module to use ``preserve_errors=True``.
2133 You can then use the ``flatten_errors`` function to turn your nested
2134 results dictionary into a flattened list of failures - useful for
2135 displaying meaningful error messages.
2138 if self.configspec is None:
2139 raise ValueError('No configspec supplied.')
2141 # We do this once to remove a top level dependency on the validate module
2142 # Which makes importing configobj faster
2143 from validate import VdtMissingValue
2144 self._vdtMissingValue = VdtMissingValue
2149 section.initial_comment = section.configspec.initial_comment
2150 section.final_comment = section.configspec.final_comment
2151 section.encoding = section.configspec.encoding
2152 section.BOM = section.configspec.BOM
2153 section.newlines = section.configspec.newlines
2154 section.indent_type = section.configspec.indent_type
2157 # section.default_values.clear() #??
2158 configspec = section.configspec
2159 self._set_configspec(section, copy)
2162 def validate_entry(entry, spec, val, missing, ret_true, ret_false):
2163 section.default_values.pop(entry, None)
2166 section.default_values[entry] = validator.get_default_value(configspec[entry])
2167 except (KeyError, AttributeError, validator.baseErrorClass):
2168 # No default, bad default or validator has no 'get_default_value'
2173 check = validator.check(spec,
2177 except validator.baseErrorClass, e:
2178 if not preserve_errors or isinstance(e, self._vdtMissingValue):
2181 # preserve the error
2188 if self.stringify or missing:
2189 # if we are doing type conversion
2190 # or the value is a supplied default
2191 if not self.stringify:
2192 if isinstance(check, (list, tuple)):
2194 check = [self._str(item) for item in check]
2195 elif missing and check is None:
2196 # convert the None from a default to a ''
2199 check = self._str(check)
2200 if (check != val) or missing:
2201 section[entry] = check
2202 if not copy and missing and entry not in section.defaults:
2203 section.defaults.append(entry)
2204 return ret_true, ret_false
2211 unvalidated = [k for k in section.scalars if k not in configspec]
2212 incorrect_sections = [k for k in configspec.sections if k in section.scalars]
2213 incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2215 for entry in configspec.scalars:
2216 if entry in ('__many__', '___many___'):
2219 if (not entry in section.scalars) or (entry in section.defaults):
2221 # or entries from defaults
2224 if copy and entry not in section.scalars:
2226 section.comments[entry] = (
2227 configspec.comments.get(entry, []))
2228 section.inline_comments[entry] = (
2229 configspec.inline_comments.get(entry, ''))
2233 val = section[entry]
2235 ret_true, ret_false = validate_entry(entry, configspec[entry], val,
2236 missing, ret_true, ret_false)
2239 if '__many__' in configspec.scalars:
2240 many = configspec['__many__']
2241 elif '___many___' in configspec.scalars:
2242 many = configspec['___many___']
2244 if many is not None:
2245 for entry in unvalidated:
2246 val = section[entry]
2247 ret_true, ret_false = validate_entry(entry, many, val, False,
2248 ret_true, ret_false)
2251 for entry in incorrect_scalars:
2253 if not preserve_errors:
2257 msg = 'Value %r was provided as a section' % entry
2258 out[entry] = validator.baseErrorClass(msg)
2259 for entry in incorrect_sections:
2261 if not preserve_errors:
2265 msg = 'Section %r was provided as a single value' % entry
2266 out[entry] = validator.baseErrorClass(msg)
2268 # Missing sections will have been created as empty ones when the
2269 # configspec was read.
2270 for entry in section.sections:
2271 # FIXME: this means DEFAULT is not copied in copy mode
2272 if section is self and entry == 'DEFAULT':
2274 if section[entry].configspec is None:
2275 unvalidated.append(entry)
2278 section.comments[entry] = configspec.comments.get(entry, [])
2279 section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
2280 check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
2289 section.extra_values = unvalidated
2290 if preserve_errors and not section._created:
2291 # If the section wasn't created (i.e. it wasn't missing)
2292 # then we can't return False, we need to preserve errors
2295 if ret_false and preserve_errors and out:
2296 # If we are preserving errors, but all
2297 # the failures are from missing sections / values
2298 # then we can return False. Otherwise there is a
2299 # real failure that we need to preserve.
2300 ret_false = not any(out.values())
2309 """Clear ConfigObj instance and restore to 'freshly created' state."""
2312 # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2313 # requires an empty dictionary
2314 self.configspec = None
2315 # Just to be sure ;-)
2316 self._original_configspec = None
2321 Reload a ConfigObj from file.
2323 This method raises a ``ReloadError`` if the ConfigObj doesn't have
2324 a filename attribute pointing to a file.
2326 if not isinstance(self.filename, basestring):
2329 filename = self.filename
2330 current_options = {}
2331 for entry in OPTION_DEFAULTS:
2332 if entry == 'configspec':
2334 current_options[entry] = getattr(self, entry)
2336 configspec = self._original_configspec
2337 current_options['configspec'] = configspec
2340 self._initialise(current_options)
2341 self._load(filename, configspec)
2345 class SimpleVal(object):
2348 Can be used to check that all members expected are present.
2350 To use it, provide a configspec with all your members in (the value given
2351 will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2352 method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2353 members are present, or a dictionary with True/False meaning
2354 present/missing. (Whole missing sections will be replaced with ``False``)
2358 self.baseErrorClass = ConfigObjError
2360 def check(self, check, member, missing=False):
2361 """A dummy check method, always returns the value unchanged."""
2363 raise self.baseErrorClass()
2367 def flatten_errors(cfg, res, levels=None, results=None):
2369 An example function that will turn a nested dictionary of results
2370 (as returned by ``ConfigObj.validate``) into a flat list.
2372 ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2373 dictionary returned by ``validate``.
2375 (This is a recursive function, so you shouldn't use the ``levels`` or
2376 ``results`` arguments - they are used by the function.)
2378 Returns a list of keys that failed. Each member of the list is a tuple::
2380 ([list of sections...], key, result)
2382 If ``validate`` was called with ``preserve_errors=False`` (the default)
2383 then ``result`` will always be ``False``.
2385 *list of sections* is a flattened list of sections that the key was found
2388 If the section was missing (or a section was expected and a scalar provided
2389 - or vice-versa) then key will be ``None``.
2391 If the value (or section) was missing then ``result`` will be ``False``.
2393 If ``validate`` was called with ``preserve_errors=True`` and a value
2394 was present, but failed the check, then ``result`` will be the exception
2395 object returned. You can use this as a string that describes the failure.
2397 For example *The value "3" is of the wrong type*.
2405 if res == False or isinstance(res, Exception):
2406 results.append((levels[:], None, res))
2410 for (key, val) in res.items():
2413 if isinstance(cfg.get(key), dict):
2416 flatten_errors(cfg[key], val, levels, results)
2418 results.append((levels[:], key, val))
2427 def get_extra_values(conf, _prepend=()):
2429 Find all the values and sections not in the configspec from a validated
2432 ``get_extra_values`` returns a list of tuples where each tuple represents
2433 either an extra section, or an extra value.
2435 The tuples contain two values, a tuple representing the section the value
2436 is in and the name of the extra values. For extra values in the top level
2437 section the first member will be an empty tuple. For values in the 'foo'
2438 section the first member will be ``('foo',)``. For members in the 'bar'
2439 subsection of the 'foo' section the first member will be ``('foo', 'bar')``.
2441 NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't
2442 been validated it will return an empty list.
2446 out.extend((_prepend, name) for name in conf.extra_values)
2447 for name in conf.sections:
2448 if name not in conf.extra_values:
2449 out.extend(get_extra_values(conf[name], _prepend + (name,)))
2453 """*A programming language is a medium of expression.* - Paul Graham"""