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
35 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
37 # Python 2.2 does not have these
39 BOM_UTF8 = '\xef\xbb\xbf'
40 # UTF-16, little endian
41 BOM_UTF16_LE = '\xff\xfe'
43 BOM_UTF16_BE = '\xfe\xff'
44 if sys.byteorder == 'little':
45 # UTF-16, native endianness
46 BOM_UTF16 = BOM_UTF16_LE
48 # UTF-16, native endianness
49 BOM_UTF16 = BOM_UTF16_BE
51 # A dictionary mapping BOM to
52 # the encoding to decode with, and what to set the
53 # encoding attribute to.
55 BOM_UTF8: ('utf_8', None),
56 BOM_UTF16_BE: ('utf16_be', 'utf_16'),
57 BOM_UTF16_LE: ('utf16_le', 'utf_16'),
58 BOM_UTF16: ('utf_16', 'utf_16'),
60 # All legal variants of the BOM codecs.
61 # TODO: the list of aliases is not meant to be exhaustive, is there a
68 'utf16_be': 'utf16_be',
69 'utf_16_be': 'utf16_be',
70 'utf-16be': 'utf16_be',
71 'utf16_le': 'utf16_le',
72 'utf_16_le': 'utf16_le',
73 'utf-16le': 'utf16_le',
81 # Map of encodings to the BOM to write.
85 'utf16_be': BOM_UTF16_BE,
86 'utf16_le': BOM_UTF16_LE,
91 def match_utf8(encoding):
92 return BOM_LIST.get(encoding.lower()) == 'utf_8'
95 # Quote strings used for writing values
99 wspace_plus = ' \r\n\v\t\'"'
107 """enumerate for Python 2.2."""
113 # Sentinel for use in getattr calls to replace hasattr
116 __version__ = '4.6.0'
118 __revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
120 __docformat__ = "restructuredtext en"
124 'DEFAULT_INDENT_TYPE',
125 'DEFAULT_INTERPOLATION',
133 'InterpolationError',
134 'InterpolationLoopError',
135 'MissingInterpolationOption',
136 'RepeatSectionError',
144 DEFAULT_INTERPOLATION = 'configparser'
145 DEFAULT_INDENT_TYPE = ' '
146 MAX_INTERPOL_DEPTH = 10
149 'interpolation': True,
150 'raise_errors': False,
152 'create_empty': False,
156 # option may be set to one of ('', ' ', '\t')
159 'default_encoding': None,
161 'write_empty_values': False,
169 raise ImportError('compiler module not available')
170 p = compiler.parse(s)
171 return p.getChildren()[1].getChildren()[0].getChildren()[1]
174 class UnknownType(Exception):
178 class Builder(object):
181 m = getattr(self, 'build_' + o.__class__.__name__, None)
183 raise UnknownType(o.__class__.__name__)
186 def build_List(self, o):
187 return map(self.build, o.getChildren())
189 def build_Const(self, o):
192 def build_Dict(self, o):
194 i = iter(map(self.build, o.getChildren()))
199 def build_Tuple(self, o):
200 return tuple(self.build_List(o))
202 def build_Name(self, o):
207 if o.name == 'False':
211 raise UnknownType('Undefined Name')
213 def build_Add(self, o):
214 real, imag = map(self.build_Const, o.getChildren())
218 raise UnknownType('Add')
219 if not isinstance(imag, complex) or imag.real != 0.0:
220 raise UnknownType('Add')
223 def build_Getattr(self, o):
224 parent = self.build(o.expr)
225 return getattr(parent, o.attrname)
227 def build_UnarySub(self, o):
228 return -self.build_Const(o.getChildren()[0])
230 def build_UnaryAdd(self, o):
231 return self.build_Const(o.getChildren()[0])
240 return _builder.build(getObj(s))
244 class ConfigObjError(SyntaxError):
246 This is the base class for all errors that ConfigObj raises.
247 It is a subclass of SyntaxError.
249 def __init__(self, message='', line_number=None, line=''):
251 self.line_number = line_number
252 SyntaxError.__init__(self, message)
255 class NestingError(ConfigObjError):
257 This error indicates a level of nesting that doesn't match.
261 class ParseError(ConfigObjError):
263 This error indicates that a line is badly written.
264 It is neither a valid ``key = value`` line,
265 nor a valid section marker line.
269 class ReloadError(IOError):
271 A 'reload' operation failed.
272 This exception is a subclass of ``IOError``.
275 IOError.__init__(self, 'reload failed, filename is not set.')
278 class DuplicateError(ConfigObjError):
280 The keyword or section specified already exists.
284 class ConfigspecError(ConfigObjError):
286 An error occured whilst parsing a configspec.
290 class InterpolationError(ConfigObjError):
291 """Base class for the two interpolation errors."""
294 class InterpolationLoopError(InterpolationError):
295 """Maximum interpolation depth exceeded in string interpolation."""
297 def __init__(self, option):
298 InterpolationError.__init__(
300 'interpolation loop detected in value "%s".' % option)
303 class RepeatSectionError(ConfigObjError):
305 This error indicates additional sections in a section with a
306 ``__many__`` (repeated) section.
310 class MissingInterpolationOption(InterpolationError):
311 """A value specified for interpolation was missing."""
313 def __init__(self, option):
314 InterpolationError.__init__(
316 'missing option "%s" in interpolation.' % option)
319 class UnreprError(ConfigObjError):
320 """An error parsing in unrepr mode."""
324 class InterpolationEngine(object):
326 A helper class to help perform string interpolation.
328 This class is an abstract base class; its descendants perform
332 # compiled regexp to use in self.interpolate()
333 _KEYCRE = re.compile(r"%\(([^)]*)\)s")
335 def __init__(self, section):
336 # the Section instance that "owns" this engine
337 self.section = section
340 def interpolate(self, key, value):
341 def recursive_interpolate(key, value, section, backtrail):
342 """The function that does the actual work.
344 ``value``: the string we're trying to interpolate.
345 ``section``: the section in which that string was found
346 ``backtrail``: a dict to keep track of where we've been,
347 to detect and prevent infinite recursion loops
349 This is similar to a depth-first-search algorithm.
351 # Have we been here already?
352 if backtrail.has_key((key, section.name)):
353 # Yes - infinite loop detected
354 raise InterpolationLoopError(key)
355 # Place a marker on our backtrail so we won't come back here again
356 backtrail[(key, section.name)] = 1
358 # Now start the actual work
359 match = self._KEYCRE.search(value)
361 # The actual parsing of the match is implementation-dependent,
362 # so delegate to our helper function
363 k, v, s = self._parse_match(match)
365 # That's the signal that no further interpolation is needed
368 # Further interpolation may be needed to obtain final value
369 replacement = recursive_interpolate(k, v, s, backtrail)
370 # Replace the matched string with its final value
371 start, end = match.span()
372 value = ''.join((value[:start], replacement, value[end:]))
373 new_search_start = start + len(replacement)
374 # Pick up the next interpolation key, if any, for next time
375 # through the while loop
376 match = self._KEYCRE.search(value, new_search_start)
378 # Now safe to come back here again; remove marker from backtrail
379 del backtrail[(key, section.name)]
383 # Back in interpolate(), all we have to do is kick off the recursive
384 # function with appropriate starting values
385 value = recursive_interpolate(key, value, self.section, {})
389 def _fetch(self, key):
390 """Helper function to fetch values from owning section.
392 Returns a 2-tuple: the value, and the section where it was found.
394 # switch off interpolation before we try and fetch anything !
395 save_interp = self.section.main.interpolation
396 self.section.main.interpolation = False
398 # Start at section that "owns" this InterpolationEngine
399 current_section = self.section
401 # try the current section first
402 val = current_section.get(key)
406 val = current_section.get('DEFAULT', {}).get(key)
409 # move up to parent and try again
410 # top-level's parent is itself
411 if current_section.parent is current_section:
412 # reached top level, time to give up
414 current_section = current_section.parent
416 # restore interpolation to previous value before returning
417 self.section.main.interpolation = save_interp
419 raise MissingInterpolationOption(key)
420 return val, current_section
423 def _parse_match(self, match):
424 """Implementation-dependent helper function.
426 Will be passed a match object corresponding to the interpolation
427 key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
428 key in the appropriate config file section (using the ``_fetch()``
429 helper function) and return a 3-tuple: (key, value, section)
431 ``key`` is the name of the key we're looking for
432 ``value`` is the value found for that key
433 ``section`` is a reference to the section where it was found
435 ``key`` and ``section`` should be None if no further
436 interpolation should be performed on the resulting value
437 (e.g., if we interpolated "$$" and returned "$").
439 raise NotImplementedError()
443 class ConfigParserInterpolation(InterpolationEngine):
444 """Behaves like ConfigParser."""
445 _KEYCRE = re.compile(r"%\(([^)]*)\)s")
447 def _parse_match(self, match):
449 value, section = self._fetch(key)
450 return key, value, section
454 class TemplateInterpolation(InterpolationEngine):
455 """Behaves like string.Template."""
457 _KEYCRE = re.compile(r"""
459 (?P<escaped>\$) | # Two $ signs
460 (?P<named>[_a-z][_a-z0-9]*) | # $name format
461 {(?P<braced>[^}]*)} # ${name} format
463 """, re.IGNORECASE | re.VERBOSE)
465 def _parse_match(self, match):
466 # Valid name (in or out of braces): fetch value from section
467 key = match.group('named') or match.group('braced')
469 value, section = self._fetch(key)
470 return key, value, section
471 # Escaped delimiter (e.g., $$): return single delimiter
472 if match.group('escaped') is not None:
473 # Return None for key and section to indicate it's time to stop
474 return None, self._delimiter, None
475 # Anything else: ignore completely, just return it unchanged
476 return None, match.group(), None
479 interpolation_engines = {
480 'configparser': ConfigParserInterpolation,
481 'template': TemplateInterpolation,
485 def __newobj__(cls, *args):
487 return cls.__new__(cls, *args)
491 A dictionary-like object that represents a section in a config file.
493 It does string interpolation if the 'interpolation' attribute
494 of the 'main' object is set to True.
496 Interpolation is tried first from this object, then from the 'DEFAULT'
497 section of this object, next from the parent and its 'DEFAULT' section,
498 and so on until the main object is reached.
500 A Section will behave like an ordered dictionary - following the
501 order of the ``scalars`` and ``sections`` attributes.
502 You can use this to change the order of members.
504 Iteration follows the order: scalars, then sections.
508 def __setstate__(self, state):
509 dict.update(self, state[0])
510 self.__dict__.update(state[1])
512 def __reduce__(self):
513 state = (dict(self), self.__dict__)
514 return (__newobj__, (self.__class__,), state)
517 def __init__(self, parent, depth, main, indict=None, name=None):
519 * parent is the section above
520 * depth is the depth level of this section
521 * main is the main ConfigObj
522 * indict is a dictionary to initialise the section with
527 # used for nesting level *and* interpolation
529 # used for the interpolation attribute
531 # level of nesting depth of this Section
533 # purely for information
537 # we do this explicitly so that __setitem__ is used properly
538 # (rather than just passing to ``dict.__init__``)
539 for entry, value in indict.iteritems():
543 def _initialise(self):
544 # the sequence of scalar values in this Section
546 # the sequence of sections in this Section
550 self.inline_comments = {}
552 self.configspec = None
555 self.default_values = {}
558 def _interpolate(self, key, value):
560 # do we already have an interpolation engine?
561 engine = self._interpolation_engine
562 except AttributeError:
563 # not yet: first time running _interpolate(), so pick the engine
564 name = self.main.interpolation
565 if name == True: # note that "if name:" would be incorrect here
566 # backwards-compatibility: interpolation=True means use default
567 name = DEFAULT_INTERPOLATION
568 name = name.lower() # so that "Template", "template", etc. all work
569 class_ = interpolation_engines.get(name, None)
571 # invalid value for self.main.interpolation
572 self.main.interpolation = False
575 # save reference to engine so we don't have to do this again
576 engine = self._interpolation_engine = class_(self)
577 # let the engine do the actual work
578 return engine.interpolate(key, value)
581 def __getitem__(self, key):
582 """Fetch the item and do string interpolation."""
583 val = dict.__getitem__(self, key)
584 if self.main.interpolation and isinstance(val, basestring):
585 return self._interpolate(key, val)
589 def __setitem__(self, key, value, unrepr=False):
591 Correctly set a value.
593 Making dictionary values Section instances.
594 (We have to special case 'Section' instances - which are also dicts)
596 Keys must be strings.
597 Values need only be strings (or lists of strings) if
598 ``main.stringify`` is set.
600 ``unrepr`` must be set when setting a value to a dictionary, without
601 creating a new sub-section.
603 if not isinstance(key, basestring):
604 raise ValueError('The key "%s" is not a string.' % key)
607 if not self.comments.has_key(key):
608 self.comments[key] = []
609 self.inline_comments[key] = ''
610 # remove the entry from defaults
611 if key in self.defaults:
612 self.defaults.remove(key)
614 if isinstance(value, Section):
615 if not self.has_key(key):
616 self.sections.append(key)
617 dict.__setitem__(self, key, value)
618 elif isinstance(value, dict) and not unrepr:
619 # First create the new depth level,
620 # then create the section
621 if not self.has_key(key):
622 self.sections.append(key)
623 new_depth = self.depth + 1
634 if not self.has_key(key):
635 self.scalars.append(key)
636 if not self.main.stringify:
637 if isinstance(value, basestring):
639 elif isinstance(value, (list, tuple)):
641 if not isinstance(entry, basestring):
642 raise TypeError('Value is not a string "%s".' % entry)
644 raise TypeError('Value is not a string "%s".' % value)
645 dict.__setitem__(self, key, value)
648 def __delitem__(self, key):
649 """Remove items from the sequence when deleting."""
650 dict. __delitem__(self, key)
651 if key in self.scalars:
652 self.scalars.remove(key)
654 self.sections.remove(key)
655 del self.comments[key]
656 del self.inline_comments[key]
659 def get(self, key, default=None):
660 """A version of ``get`` that doesn't bypass string interpolation."""
667 def update(self, indict):
669 A version of update that uses our ``__setitem__``.
672 self[entry] = indict[entry]
675 def pop(self, key, *args):
677 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
678 If key is not found, d is returned if given, otherwise KeyError is raised'
680 val = dict.pop(self, key, *args)
681 if key in self.scalars:
682 del self.comments[key]
683 del self.inline_comments[key]
684 self.scalars.remove(key)
685 elif key in self.sections:
686 del self.comments[key]
687 del self.inline_comments[key]
688 self.sections.remove(key)
689 if self.main.interpolation and isinstance(val, basestring):
690 return self._interpolate(key, val)
695 """Pops the first (key,val)"""
696 sequence = (self.scalars + self.sections)
698 raise KeyError(": 'popitem(): dictionary is empty'")
707 A version of clear that also affects scalars/sections
708 Also clears comments and configspec.
710 Leaves other attributes alone :
711 depth/main/parent are not affected
717 self.inline_comments = {}
718 self.configspec = None
721 def setdefault(self, key, default=None):
722 """A version of setdefault that sets sequence if appropriate."""
731 """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
732 return zip((self.scalars + self.sections), self.values())
736 """D.keys() -> list of D's keys"""
737 return (self.scalars + self.sections)
741 """D.values() -> list of D's values"""
742 return [self[key] for key in (self.scalars + self.sections)]
746 """D.iteritems() -> an iterator over the (key, value) items of D"""
747 return iter(self.items())
751 """D.iterkeys() -> an iterator over the keys of D"""
752 return iter((self.scalars + self.sections))
757 def itervalues(self):
758 """D.itervalues() -> an iterator over the values of D"""
759 return iter(self.values())
763 """x.__repr__() <==> repr(x)"""
764 return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
765 for key in (self.scalars + self.sections)])
768 __str__.__doc__ = "x.__str__() <==> str(x)"
771 # Extra methods - not in a normal dictionary
775 Return a deepcopy of self as a dictionary.
777 All members that are ``Section`` instances are recursively turned to
778 ordinary dictionaries - by calling their ``dict`` method.
788 this_entry = self[entry]
789 if isinstance(this_entry, Section):
790 this_entry = this_entry.dict()
791 elif isinstance(this_entry, list):
792 # create a copy rather than a reference
793 this_entry = list(this_entry)
794 elif isinstance(this_entry, tuple):
795 # create a copy rather than a reference
796 this_entry = tuple(this_entry)
797 newdict[entry] = this_entry
801 def merge(self, indict):
803 A recursive update - useful for merging config files.
805 >>> a = '''[section1]
808 ... more_options = False
809 ... # end of file'''.splitlines()
810 >>> b = '''# File is user.ini
813 ... # end of file'''.splitlines()
814 >>> c1 = ConfigObj(b)
815 >>> c2 = ConfigObj(a)
818 ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
820 for key, val in indict.items():
821 if (key in self and isinstance(self[key], dict) and
822 isinstance(val, dict)):
828 def rename(self, oldkey, newkey):
830 Change a keyname to another, without changing position in sequence.
832 Implemented so that transformations can be made on keys,
833 as well as on values. (used by encode and decode)
835 Also renames comments.
837 if oldkey in self.scalars:
838 the_list = self.scalars
839 elif oldkey in self.sections:
840 the_list = self.sections
842 raise KeyError('Key "%s" not found.' % oldkey)
843 pos = the_list.index(oldkey)
846 dict.__delitem__(self, oldkey)
847 dict.__setitem__(self, newkey, val)
848 the_list.remove(oldkey)
849 the_list.insert(pos, newkey)
850 comm = self.comments[oldkey]
851 inline_comment = self.inline_comments[oldkey]
852 del self.comments[oldkey]
853 del self.inline_comments[oldkey]
854 self.comments[newkey] = comm
855 self.inline_comments[newkey] = inline_comment
858 def walk(self, function, raise_errors=True,
859 call_on_sections=False, **keywargs):
861 Walk every member and call a function on the keyword and value.
863 Return a dictionary of the return values
865 If the function raises an exception, raise the errror
866 unless ``raise_errors=False``, in which case set the return value to
869 Any unrecognised keyword arguments you pass to walk, will be pased on
870 to the function you pass in.
872 Note: if ``call_on_sections`` is ``True`` then - on encountering a
873 subsection, *first* the function is called for the *whole* subsection,
874 and then recurses into it's members. This means your function must be
875 able to handle strings, dictionaries and lists. This allows you
876 to change the key of subsections as well as for ordinary members. The
877 return value when called on the whole subsection has to be discarded.
879 See the encode and decode methods for examples, including functions.
881 .. admonition:: caution
883 You can use ``walk`` to transform the names of members of a section
884 but you mustn't add or delete members.
886 >>> config = '''[XXXXsection]
887 ... XXXXkey = XXXXvalue'''.splitlines()
888 >>> cfg = ConfigObj(config)
890 ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
891 >>> def transform(section, key):
892 ... val = section[key]
893 ... newkey = key.replace('XXXX', 'CLIENT1')
894 ... section.rename(key, newkey)
895 ... if isinstance(val, (tuple, list, dict)):
898 ... val = val.replace('XXXX', 'CLIENT1')
899 ... section[newkey] = val
900 >>> cfg.walk(transform, call_on_sections=True)
901 {'CLIENT1section': {'CLIENT1key': None}}
903 ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
907 for i in range(len(self.scalars)):
908 entry = self.scalars[i]
910 val = function(self, entry, **keywargs)
911 # bound again in case name has changed
912 entry = self.scalars[i]
918 entry = self.scalars[i]
921 for i in range(len(self.sections)):
922 entry = self.sections[i]
925 function(self, entry, **keywargs)
930 entry = self.sections[i]
932 # bound again in case name has changed
933 entry = self.sections[i]
934 # previous result is discarded
935 out[entry] = self[entry].walk(
937 raise_errors=raise_errors,
938 call_on_sections=call_on_sections,
943 def as_bool(self, key):
945 Accepts a key as input. The corresponding value must be a string or
946 the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
947 retain compatibility with Python 2.2.
949 If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
952 If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
955 ``as_bool`` is not case sensitive.
957 Any other input will raise a ``ValueError``.
962 Traceback (most recent call last):
963 ValueError: Value "fish" is neither True nor False
978 if not isinstance(val, basestring):
979 # TODO: Why do we raise a KeyError here?
982 return self.main._bools[val.lower()]
984 raise ValueError('Value "%s" is neither True nor False' % val)
987 def as_int(self, key):
989 A convenience method which coerces the specified value to an integer.
991 If the value is an invalid literal for ``int``, a ``ValueError`` will
997 Traceback (most recent call last):
998 ValueError: invalid literal for int() with base 10: 'fish'
1004 Traceback (most recent call last):
1005 ValueError: invalid literal for int() with base 10: '3.2'
1007 return int(self[key])
1010 def as_float(self, key):
1012 A convenience method which coerces the specified value to a float.
1014 If the value is an invalid literal for ``float``, a ``ValueError`` will
1020 Traceback (most recent call last):
1021 ValueError: invalid literal for float(): fish
1029 return float(self[key])
1032 def as_list(self, key):
1034 A convenience method which fetches the specified value, guaranteeing
1049 if isinstance(result, (tuple, list)):
1054 def restore_default(self, key):
1056 Restore (and return) default value for the specified key.
1058 This method will only work for a ConfigObj that was created
1059 with a configspec and has been validated.
1061 If there is no default value for this key, ``KeyError`` is raised.
1063 default = self.default_values[key]
1064 dict.__setitem__(self, key, default)
1065 if key not in self.defaults:
1066 self.defaults.append(key)
1070 def restore_defaults(self):
1072 Recursively restore default values to all members
1075 This method will only work for a ConfigObj that was created
1076 with a configspec and has been validated.
1078 It doesn't delete or modify entries without default values.
1080 for key in self.default_values:
1081 self.restore_default(key)
1083 for section in self.sections:
1084 self[section].restore_defaults()
1087 class ConfigObj(Section):
1088 """An object to read, create, and write config files."""
1090 _keyword = re.compile(r'''^ # line start
1093 (?:".*?")| # double quotes
1094 (?:'.*?')| # single quotes
1095 (?:[^'"=].*?) # no quotes
1098 (.*) # value (including list values and comments)
1103 _sectionmarker = re.compile(r'''^
1104 (\s*) # 1: indentation
1105 ((?:\[\s*)+) # 2: section marker open
1106 ( # 3: section name open
1107 (?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1108 (?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1109 (?:[^'"\s].*?) # at least one non-space unquoted
1110 ) # section name close
1111 ((?:\s*\])+) # 4: section marker close
1112 \s*(\#.*)? # 5: optional comment
1116 # this regexp pulls list values out as a single string
1117 # or single values and comments
1118 # FIXME: this regex adds a '' to the end of comma terminated lists
1119 # workaround in ``_handle_value``
1120 _valueexp = re.compile(r'''^
1126 (?:".*?")| # double quotes
1127 (?:'.*?')| # single quotes
1128 (?:[^'",\#][^,\#]*?) # unquoted
1131 )* # match all list items ending in a comma (if any)
1134 (?:".*?")| # double quotes
1135 (?:'.*?')| # single quotes
1136 (?:[^'",\#\s][^,]*?)| # unquoted
1137 (?:(?<!,)) # Empty value
1138 )? # last item in a list - or string value
1140 (,) # alternatively a single comma - empty list
1142 \s*(\#.*)? # optional comment
1146 # use findall to get the members of a list value
1147 _listvalueexp = re.compile(r'''
1149 (?:".*?")| # double quotes
1150 (?:'.*?')| # single quotes
1151 (?:[^'",\#].*?) # unquoted
1157 # this regexp is used for the value
1158 # when lists are switched off
1159 _nolistvalue = re.compile(r'''^
1161 (?:".*?")| # double quotes
1162 (?:'.*?')| # single quotes
1163 (?:[^'"\#].*?)| # unquoted
1166 \s*(\#.*)? # optional comment
1170 # regexes for finding triple quoted values on one line
1171 _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1172 _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1173 _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1174 _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1177 "'''": (_single_line_single, _multi_line_single),
1178 '"""': (_single_line_double, _multi_line_double),
1181 # Used by the ``istrue`` Section method
1183 'yes': True, 'no': False,
1184 'on': True, 'off': False,
1185 '1': True, '0': False,
1186 'true': True, 'false': False,
1190 def __init__(self, infile=None, options=None, _inspec=False, **kwargs):
1192 Parse a config file or create a config file object.
1194 ``ConfigObj(infile=None, options=None, **kwargs)``
1196 self._inspec = _inspec
1197 # init the superclass
1198 Section.__init__(self, self, 0, self)
1200 infile = infile or []
1201 options = dict(options or {})
1203 # keyword arguments take precedence over an options dictionary
1204 options.update(kwargs)
1206 options['list_values'] = False
1208 defaults = OPTION_DEFAULTS.copy()
1209 # TODO: check the values too.
1210 for entry in options:
1211 if entry not in defaults:
1212 raise TypeError('Unrecognised option "%s".' % entry)
1214 # Add any explicit options to the defaults
1215 defaults.update(options)
1216 self._initialise(defaults)
1217 configspec = defaults['configspec']
1218 self._original_configspec = configspec
1219 self._load(infile, configspec)
1222 def _load(self, infile, configspec):
1223 if isinstance(infile, basestring):
1224 self.filename = infile
1225 if os.path.isfile(infile):
1226 h = open(infile, 'rb')
1227 infile = h.read() or []
1229 elif self.file_error:
1230 # raise an error if the file doesn't exist
1231 raise IOError('Config file not found: "%s".' % self.filename)
1233 # file doesn't already exist
1234 if self.create_empty:
1235 # this is a good test that the filename specified
1236 # isn't impossible - like on a non-existent device
1237 h = open(infile, 'w')
1242 elif isinstance(infile, (list, tuple)):
1243 infile = list(infile)
1245 elif isinstance(infile, dict):
1247 # the Section class handles creating subsections
1248 if isinstance(infile, ConfigObj):
1249 # get a copy of our ConfigObj
1250 infile = infile.dict()
1252 for entry in infile:
1253 self[entry] = infile[entry]
1256 if configspec is not None:
1257 self._handle_configspec(configspec)
1259 self.configspec = None
1262 elif getattr(infile, 'read', MISSING) is not MISSING:
1263 # This supports file like objects
1264 infile = infile.read() or []
1265 # needs splitting into lines - but needs doing *after* decoding
1266 # in case it's not an 8 bit encoding
1268 raise TypeError('infile must be a filename, file like object, or list of lines.')
1271 # don't do it for the empty ConfigObj
1272 infile = self._handle_bom(infile)
1273 # infile is now *always* a list
1275 # Set the newlines attribute (first line ending it finds)
1276 # and strip trailing '\n' or '\r' from lines
1278 if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1280 for end in ('\r\n', '\n', '\r'):
1281 if line.endswith(end):
1286 infile = [line.rstrip('\r\n') for line in infile]
1289 # if we had any errors, now is the time to raise them
1291 info = "at line %s." % self._errors[0].line_number
1292 if len(self._errors) > 1:
1293 msg = "Parsing failed with several errors.\nFirst error %s" % info
1294 error = ConfigObjError(msg)
1296 error = self._errors[0]
1297 # set the errors attribute; it's a list of tuples:
1298 # (error_type, message, line_number)
1299 error.errors = self._errors
1300 # set the config attribute
1303 # delete private attributes
1306 if configspec is None:
1307 self.configspec = None
1309 self._handle_configspec(configspec)
1312 def _initialise(self, options=None):
1314 options = OPTION_DEFAULTS
1316 # initialise a few variables
1317 self.filename = None
1319 self.raise_errors = options['raise_errors']
1320 self.interpolation = options['interpolation']
1321 self.list_values = options['list_values']
1322 self.create_empty = options['create_empty']
1323 self.file_error = options['file_error']
1324 self.stringify = options['stringify']
1325 self.indent_type = options['indent_type']
1326 self.encoding = options['encoding']
1327 self.default_encoding = options['default_encoding']
1329 self.newlines = None
1330 self.write_empty_values = options['write_empty_values']
1331 self.unrepr = options['unrepr']
1333 self.initial_comment = []
1334 self.final_comment = []
1335 self.configspec = None
1338 self.list_values = False
1340 # Clear section attributes as well
1341 Section._initialise(self)
1345 return ('ConfigObj({%s})' %
1346 ', '.join([('%s: %s' % (repr(key), repr(self[key])))
1347 for key in (self.scalars + self.sections)]))
1350 def _handle_bom(self, infile):
1352 Handle any BOM, and decode if necessary.
1354 If an encoding is specified, that *must* be used - but the BOM should
1355 still be removed (and the BOM attribute set).
1357 (If the encoding is wrongly specified, then a BOM for an alternative
1358 encoding won't be discovered or removed.)
1360 If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1361 removed. The BOM attribute will be set. UTF16 will be decoded to
1364 NOTE: This method must not be called with an empty ``infile``.
1366 Specifying the *wrong* encoding is likely to cause a
1367 ``UnicodeDecodeError``.
1369 ``infile`` must always be returned as a list of lines, but may be
1370 passed in as a single string.
1372 if ((self.encoding is not None) and
1373 (self.encoding.lower() not in BOM_LIST)):
1374 # No need to check for a BOM
1375 # the encoding specified doesn't have one
1377 return self._decode(infile, self.encoding)
1379 if isinstance(infile, (list, tuple)):
1383 if self.encoding is not None:
1384 # encoding explicitly supplied
1385 # And it could have an associated BOM
1386 # TODO: if encoding is just UTF16 - we ought to check for both
1387 # TODO: big endian and little endian versions.
1388 enc = BOM_LIST[self.encoding.lower()]
1390 # For UTF16 we try big endian and little endian
1391 for BOM, (encoding, final_encoding) in BOMS.items():
1392 if not final_encoding:
1395 if infile.startswith(BOM):
1398 # Don't need to remove BOM
1399 return self._decode(infile, encoding)
1401 # If we get this far, will *probably* raise a DecodeError
1402 # As it doesn't appear to start with a BOM
1403 return self._decode(infile, self.encoding)
1407 if not line.startswith(BOM):
1408 return self._decode(infile, self.encoding)
1410 newline = line[len(BOM):]
1413 if isinstance(infile, (list, tuple)):
1418 return self._decode(infile, self.encoding)
1420 # No encoding specified - so we need to check for UTF8/UTF16
1421 for BOM, (encoding, final_encoding) in BOMS.items():
1422 if not line.startswith(BOM):
1426 self.encoding = final_encoding
1427 if not final_encoding:
1431 newline = line[len(BOM):]
1432 if isinstance(infile, (list, tuple)):
1436 # UTF8 - don't decode
1437 if isinstance(infile, basestring):
1438 return infile.splitlines(True)
1441 # UTF16 - have to decode
1442 return self._decode(infile, encoding)
1444 # No BOM discovered and no encoding specified, just return
1445 if isinstance(infile, basestring):
1446 # infile read from a file will be a single string
1447 return infile.splitlines(True)
1451 def _a_to_u(self, aString):
1452 """Decode ASCII strings to unicode if a self.encoding is specified."""
1454 return aString.decode('ascii')
1459 def _decode(self, infile, encoding):
1461 Decode infile to unicode. Using the specified encoding.
1463 if is a string, it also needs converting to a list.
1465 if isinstance(infile, basestring):
1467 # NOTE: Could raise a ``UnicodeDecodeError``
1468 return infile.decode(encoding).splitlines(True)
1469 for i, line in enumerate(infile):
1470 if not isinstance(line, unicode):
1471 # NOTE: The isinstance test here handles mixed lists of unicode/string
1472 # NOTE: But the decode will break on any non-string values
1473 # NOTE: Or could raise a ``UnicodeDecodeError``
1474 infile[i] = line.decode(encoding)
1478 def _decode_element(self, line):
1479 """Decode element to unicode if necessary."""
1480 if not self.encoding:
1482 if isinstance(line, str) and self.default_encoding:
1483 return line.decode(self.default_encoding)
1487 def _str(self, value):
1489 Used by ``stringify`` within validate, to turn non-string values
1492 if not isinstance(value, basestring):
1498 def _parse(self, infile):
1499 """Actually parse the config file."""
1500 temp_list_values = self.list_values
1502 self.list_values = False
1507 maxline = len(infile) - 1
1509 reset_comment = False
1511 while cur_index < maxline:
1515 line = infile[cur_index]
1516 sline = line.strip()
1517 # do we have anything on the line ?
1518 if not sline or sline.startswith('#'):
1519 reset_comment = False
1520 comment_list.append(line)
1524 # preserve initial comment
1525 self.initial_comment = comment_list
1529 reset_comment = True
1530 # first we check if it's a section marker
1531 mat = self._sectionmarker.match(line)
1534 (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1535 if indent and (self.indent_type is None):
1536 self.indent_type = indent
1537 cur_depth = sect_open.count('[')
1538 if cur_depth != sect_close.count(']'):
1539 self._handle_error("Cannot compute the section depth at line %s.",
1540 NestingError, infile, cur_index)
1543 if cur_depth < this_section.depth:
1544 # the new section is dropping back to a previous level
1546 parent = self._match_depth(this_section,
1549 self._handle_error("Cannot compute nesting level at line %s.",
1550 NestingError, infile, cur_index)
1552 elif cur_depth == this_section.depth:
1553 # the new section is a sibling of the current section
1554 parent = this_section.parent
1555 elif cur_depth == this_section.depth + 1:
1556 # the new section is a child the current section
1557 parent = this_section
1559 self._handle_error("Section too nested at line %s.",
1560 NestingError, infile, cur_index)
1562 sect_name = self._unquote(sect_name)
1563 if parent.has_key(sect_name):
1564 self._handle_error('Duplicate section name at line %s.',
1565 DuplicateError, infile, cur_index)
1568 # create the new section
1569 this_section = Section(
1574 parent[sect_name] = this_section
1575 parent.inline_comments[sect_name] = comment
1576 parent.comments[sect_name] = comment_list
1579 # it's not a section marker,
1580 # so it should be a valid ``key = value`` line
1581 mat = self._keyword.match(line)
1583 # it neither matched as a keyword
1584 # or a section marker
1586 'Invalid line at line "%s".',
1587 ParseError, infile, cur_index)
1589 # is a keyword value
1590 # value will include any inline comment
1591 (indent, key, value) = mat.groups()
1592 if indent and (self.indent_type is None):
1593 self.indent_type = indent
1594 # check for a multiline value
1595 if value[:3] in ['"""', "'''"]:
1597 (value, comment, cur_index) = self._multiline(
1598 value, infile, cur_index, maxline)
1601 'Parse error in value at line %s.',
1602 ParseError, infile, cur_index)
1608 value = unrepr(value)
1609 except Exception, e:
1610 if type(e) == UnknownType:
1611 msg = 'Unknown name or type in value at line %s.'
1613 msg = 'Parse error in value at line %s.'
1614 self._handle_error(msg, UnreprError, infile,
1621 value = unrepr(value)
1622 except Exception, e:
1623 if isinstance(e, UnknownType):
1624 msg = 'Unknown name or type in value at line %s.'
1626 msg = 'Parse error in value at line %s.'
1627 self._handle_error(msg, UnreprError, infile,
1631 # extract comment and lists
1633 (value, comment) = self._handle_value(value)
1636 'Parse error in value at line %s.',
1637 ParseError, infile, cur_index)
1640 key = self._unquote(key)
1641 if this_section.has_key(key):
1643 'Duplicate keyword name at line %s.',
1644 DuplicateError, infile, cur_index)
1647 # we set unrepr because if we have got this far we will never
1648 # be creating a new section
1649 this_section.__setitem__(key, value, unrepr=True)
1650 this_section.inline_comments[key] = comment
1651 this_section.comments[key] = comment_list
1654 if self.indent_type is None:
1655 # no indentation used, set the type accordingly
1656 self.indent_type = ''
1658 # preserve the final comment
1659 if not self and not self.initial_comment:
1660 self.initial_comment = comment_list
1661 elif not reset_comment:
1662 self.final_comment = comment_list
1663 self.list_values = temp_list_values
1666 def _match_depth(self, sect, depth):
1668 Given a section and a depth level, walk back through the sections
1669 parents to see if the depth level matches a previous section.
1671 Return a reference to the right section,
1672 or raise a SyntaxError.
1674 while depth < sect.depth:
1675 if sect is sect.parent:
1676 # we've reached the top level already
1679 if sect.depth == depth:
1681 # shouldn't get here
1685 def _handle_error(self, text, ErrorClass, infile, cur_index):
1687 Handle an error according to the error settings.
1689 Either raise the error or store it.
1690 The error will have occured at ``cur_index``
1692 line = infile[cur_index]
1694 message = text % cur_index
1695 error = ErrorClass(message, cur_index, line)
1696 if self.raise_errors:
1697 # raise the error - parsing stops here
1700 # reraise when parsing has finished
1701 self._errors.append(error)
1704 def _unquote(self, value):
1705 """Return an unquoted version of a value"""
1706 if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1711 def _quote(self, value, multiline=True):
1713 Return a safely quoted version of a value.
1715 Raise a ConfigObjError if the value cannot be safely quoted.
1716 If multiline is ``True`` (default) then use triple quotes
1719 * Don't quote values that don't need it.
1720 * Recursively quote members of a list and return a comma joined list.
1721 * Multiline is ``False`` for lists.
1722 * Obey list syntax for empty and single member lists.
1724 If ``list_values=False`` then the value is only quoted if it contains
1725 a ``\\n`` (is multiline) or '#'.
1727 If ``write_empty_values`` is set, and the value is an empty string, it
1730 if multiline and self.write_empty_values and value == '':
1731 # Only if multiline is set, so that it is used for values not
1732 # keys, and not values that are part of a list
1735 if multiline and isinstance(value, (list, tuple)):
1738 elif len(value) == 1:
1739 return self._quote(value[0], multiline=False) + ','
1740 return ', '.join([self._quote(val, multiline=False)
1742 if not isinstance(value, basestring):
1746 raise TypeError('Value "%s" is not a string.' % value)
1751 no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1752 need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1753 hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1754 check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1756 if check_for_single:
1757 if not self.list_values:
1758 # we don't quote if ``list_values=False``
1760 # for normal values either single or double quotes will do
1762 # will only happen if multiline is off - e.g. '\n' in key
1763 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1764 elif ((value[0] not in wspace_plus) and
1765 (value[-1] not in wspace_plus) and
1766 (',' not in value)):
1769 quot = self._get_single_quote(value)
1771 # if value has '\n' or "'" *and* '"', it will need triple quotes
1772 quot = self._get_triple_quote(value)
1774 if quot == noquot and '#' in value and self.list_values:
1775 quot = self._get_single_quote(value)
1780 def _get_single_quote(self, value):
1781 if ("'" in value) and ('"' in value):
1782 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1790 def _get_triple_quote(self, value):
1791 if (value.find('"""') != -1) and (value.find("'''") != -1):
1792 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1793 if value.find('"""') == -1:
1800 def _handle_value(self, value):
1802 Given a value string, unquote, remove comment,
1803 handle lists. (including empty and single member lists)
1806 # Parsing a configspec so don't handle comments
1808 # do we look for lists in values ?
1809 if not self.list_values:
1810 mat = self._nolistvalue.match(value)
1813 # NOTE: we don't unquote here
1816 mat = self._valueexp.match(value)
1818 # the value is badly constructed, probably badly quoted,
1819 # or an invalid list
1821 (list_values, single, empty_list, comment) = mat.groups()
1822 if (list_values == '') and (single is None):
1823 # change this if you want to accept empty values
1825 # NOTE: note there is no error handling from here if the regex
1826 # is wrong: then incorrect values will slip through
1827 if empty_list is not None:
1828 # the single comma - meaning an empty list
1829 return ([], comment)
1830 if single is not None:
1831 # handle empty values
1832 if list_values and not single:
1833 # FIXME: the '' is a workaround because our regex now matches
1834 # '' at the end of a list if it has a trailing comma
1837 single = single or '""'
1838 single = self._unquote(single)
1839 if list_values == '':
1841 return (single, comment)
1842 the_list = self._listvalueexp.findall(list_values)
1843 the_list = [self._unquote(val) for val in the_list]
1844 if single is not None:
1845 the_list += [single]
1846 return (the_list, comment)
1849 def _multiline(self, value, infile, cur_index, maxline):
1850 """Extract the value, where we are in a multiline situation."""
1852 newvalue = value[3:]
1853 single_line = self._triple_quote[quot][0]
1854 multi_line = self._triple_quote[quot][1]
1855 mat = single_line.match(value)
1857 retval = list(mat.groups())
1858 retval.append(cur_index)
1860 elif newvalue.find(quot) != -1:
1861 # somehow the triple quote is missing
1864 while cur_index < maxline:
1867 line = infile[cur_index]
1868 if line.find(quot) == -1:
1871 # end of multiline, process it
1874 # we've got to the end of the config, oops...
1876 mat = multi_line.match(line)
1878 # a badly formed line
1880 (value, comment) = mat.groups()
1881 return (newvalue + value, comment, cur_index)
1884 def _handle_configspec(self, configspec):
1885 """Parse the configspec."""
1886 # FIXME: Should we check that the configspec was created with the
1887 # correct settings ? (i.e. ``list_values=False``)
1888 if not isinstance(configspec, ConfigObj):
1890 configspec = ConfigObj(configspec,
1894 except ConfigObjError, e:
1895 # FIXME: Should these errors have a reference
1896 # to the already parsed ConfigObj ?
1897 raise ConfigspecError('Parsing configspec failed: %s' % e)
1899 raise IOError('Reading configspec failed: %s' % e)
1901 self.configspec = configspec
1905 def _set_configspec(self, section, copy):
1907 Called by validate. Handles setting the configspec on subsections
1908 including sections to be validated by __many__
1910 configspec = section.configspec
1911 many = configspec.get('__many__')
1912 if isinstance(many, dict):
1913 for entry in section.sections:
1914 if entry not in configspec:
1915 section[entry].configspec = many
1917 for entry in configspec.sections:
1918 if entry == '__many__':
1920 if entry not in section:
1924 section.comments[entry] = configspec.comments.get(entry, [])
1925 section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1927 # Could be a scalar when we expect a section
1928 if isinstance(section[entry], Section):
1929 section[entry].configspec = configspec[entry]
1932 def _write_line(self, indent_string, entry, this_entry, comment):
1933 """Write an individual line, for the write method"""
1934 # NOTE: the calls to self._quote here handles non-StringType values.
1936 val = self._decode_element(self._quote(this_entry))
1938 val = repr(this_entry)
1939 return '%s%s%s%s%s' % (indent_string,
1940 self._decode_element(self._quote(entry, multiline=False)),
1941 self._a_to_u(' = '),
1943 self._decode_element(comment))
1946 def _write_marker(self, indent_string, depth, entry, comment):
1947 """Write a section marker line"""
1948 return '%s%s%s%s%s' % (indent_string,
1949 self._a_to_u('[' * depth),
1950 self._quote(self._decode_element(entry), multiline=False),
1951 self._a_to_u(']' * depth),
1952 self._decode_element(comment))
1955 def _handle_comment(self, comment):
1956 """Deal with a comment."""
1959 start = self.indent_type
1960 if not comment.startswith('#'):
1961 start += self._a_to_u(' # ')
1962 return (start + comment)
1967 def write(self, outfile=None, section=None):
1969 Write the current ConfigObj as a file
1971 tekNico: FIXME: use StringIO instead of real files
1973 >>> filename = a.filename
1974 >>> a.filename = 'test.ini'
1976 >>> a.filename = filename
1977 >>> a == ConfigObj('test.ini', raise_errors=True)
1980 if self.indent_type is None:
1981 # this can be true if initialised from a dictionary
1982 self.indent_type = DEFAULT_INDENT_TYPE
1985 cs = self._a_to_u('#')
1986 csp = self._a_to_u('# ')
1988 int_val = self.interpolation
1989 self.interpolation = False
1991 for line in self.initial_comment:
1992 line = self._decode_element(line)
1993 stripped_line = line.strip()
1994 if stripped_line and not stripped_line.startswith(cs):
1998 indent_string = self.indent_type * section.depth
1999 for entry in (section.scalars + section.sections):
2000 if entry in section.defaults:
2001 # don't write out default values
2003 for comment_line in section.comments[entry]:
2004 comment_line = self._decode_element(comment_line.lstrip())
2005 if comment_line and not comment_line.startswith(cs):
2006 comment_line = csp + comment_line
2007 out.append(indent_string + comment_line)
2008 this_entry = section[entry]
2009 comment = self._handle_comment(section.inline_comments[entry])
2011 if isinstance(this_entry, dict):
2013 out.append(self._write_marker(
2018 out.extend(self.write(section=this_entry))
2020 out.append(self._write_line(
2027 for line in self.final_comment:
2028 line = self._decode_element(line)
2029 stripped_line = line.strip()
2030 if stripped_line and not stripped_line.startswith(cs):
2033 self.interpolation = int_val
2035 if section is not self:
2038 if (self.filename is None) and (outfile is None):
2039 # output a list of lines
2040 # might need to encode
2041 # NOTE: This will *screw* UTF16, each line will start with the BOM
2043 out = [l.encode(self.encoding) for l in out]
2044 if (self.BOM and ((self.encoding is None) or
2045 (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2049 out[0] = BOM_UTF8 + out[0]
2052 # Turn the list to a string, joined with correct newlines
2053 newline = self.newlines or os.linesep
2054 output = self._a_to_u(newline).join(out)
2056 output = output.encode(self.encoding)
2057 if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2059 output = BOM_UTF8 + output
2061 if not output.endswith(newline):
2063 if outfile is not None:
2064 outfile.write(output)
2066 h = open(self.filename, 'wb')
2071 def validate(self, validator, preserve_errors=False, copy=False,
2074 Test the ConfigObj against a configspec.
2076 It uses the ``validator`` object from *validate.py*.
2078 To run ``validate`` on the current ConfigObj, call: ::
2080 test = config.validate(validator)
2082 (Normally having previously passed in the configspec when the ConfigObj
2083 was created - you can dynamically assign a dictionary of checks to the
2084 ``configspec`` attribute of a section though).
2086 It returns ``True`` if everything passes, or a dictionary of
2087 pass/fails (True/False). If every member of a subsection passes, it
2088 will just have the value ``True``. (It also returns ``False`` if all
2091 In addition, it converts the values from strings to their native
2092 types if their checks pass (and ``stringify`` is set).
2094 If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2095 of a marking a fail with a ``False``, it will preserve the actual
2096 exception object. This can contain info about the reason for failure.
2097 For example the ``VdtValueTooSmallError`` indicates that the value
2098 supplied was too small. If a value (or section) is missing it will
2099 still be marked as ``False``.
2101 You must have the validate module to use ``preserve_errors=True``.
2103 You can then use the ``flatten_errors`` function to turn your nested
2104 results dictionary into a flattened list of failures - useful for
2105 displaying meaningful error messages.
2108 if self.configspec is None:
2109 raise ValueError('No configspec supplied.')
2111 # We do this once to remove a top level dependency on the validate module
2112 # Which makes importing configobj faster
2113 from validate import VdtMissingValue
2114 self._vdtMissingValue = VdtMissingValue
2119 section.initial_comment = section.configspec.initial_comment
2120 section.final_comment = section.configspec.final_comment
2121 section.encoding = section.configspec.encoding
2122 section.BOM = section.configspec.BOM
2123 section.newlines = section.configspec.newlines
2124 section.indent_type = section.configspec.indent_type
2127 configspec = section.configspec
2128 self._set_configspec(section, copy)
2130 def validate_entry(entry, spec, val, missing, ret_true, ret_false):
2132 check = validator.check(spec,
2136 except validator.baseErrorClass, e:
2137 if not preserve_errors or isinstance(e, self._vdtMissingValue):
2140 # preserve the error
2146 section.default_values.pop(entry, None)
2147 except AttributeError:
2148 # For Python 2.2 compatibility
2150 del section.default_values[entry]
2155 section.default_values[entry] = validator.get_default_value(configspec[entry])
2156 except (KeyError, AttributeError):
2157 # No default or validator has no 'get_default_value' (e.g. SimpleVal)
2162 if self.stringify or missing:
2163 # if we are doing type conversion
2164 # or the value is a supplied default
2165 if not self.stringify:
2166 if isinstance(check, (list, tuple)):
2168 check = [self._str(item) for item in check]
2169 elif missing and check is None:
2170 # convert the None from a default to a ''
2173 check = self._str(check)
2174 if (check != val) or missing:
2175 section[entry] = check
2176 if not copy and missing and entry not in section.defaults:
2177 section.defaults.append(entry)
2178 return ret_true, ret_false
2185 unvalidated = [k for k in section.scalars if k not in configspec]
2186 incorrect_sections = [k for k in configspec.sections if k in section.scalars]
2187 incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2189 for entry in configspec.scalars:
2190 if entry in ('__many__', '___many___'):
2194 if (not entry in section.scalars) or (entry in section.defaults):
2196 # or entries from defaults
2199 if copy and not entry in section.scalars:
2201 section.comments[entry] = (
2202 configspec.comments.get(entry, []))
2203 section.inline_comments[entry] = (
2204 configspec.inline_comments.get(entry, ''))
2208 val = section[entry]
2210 ret_true, ret_false = validate_entry(entry, configspec[entry], val,
2211 missing, ret_true, ret_false)
2214 if '__many__' in configspec.scalars:
2215 many = configspec['__many__']
2216 elif '___many___' in configspec.scalars:
2217 many = configspec['___many___']
2219 if many is not None:
2220 for entry in unvalidated:
2221 val = section[entry]
2222 ret_true, ret_false = validate_entry(entry, many, val, False,
2223 ret_true, ret_false)
2225 for entry in incorrect_scalars:
2227 if not preserve_errors:
2231 msg = 'Value %r was provided as a section' % entry
2232 out[entry] = validator.baseErrorClass(msg)
2233 for entry in incorrect_sections:
2235 if not preserve_errors:
2239 msg = 'Section %r was provided as a single value' % entry
2240 out[entry] = validator.baseErrorClass(msg)
2242 # Missing sections will have been created as empty ones when the
2243 # configspec was read.
2244 for entry in section.sections:
2245 # FIXME: this means DEFAULT is not copied in copy mode
2246 if section is self and entry == 'DEFAULT':
2248 if section[entry].configspec is None:
2251 section.comments[entry] = configspec.comments.get(entry, [])
2252 section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
2253 check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
2271 """Clear ConfigObj instance and restore to 'freshly created' state."""
2274 # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2275 # requires an empty dictionary
2276 self.configspec = None
2277 # Just to be sure ;-)
2278 self._original_configspec = None
2283 Reload a ConfigObj from file.
2285 This method raises a ``ReloadError`` if the ConfigObj doesn't have
2286 a filename attribute pointing to a file.
2288 if not isinstance(self.filename, basestring):
2291 filename = self.filename
2292 current_options = {}
2293 for entry in OPTION_DEFAULTS:
2294 if entry == 'configspec':
2296 current_options[entry] = getattr(self, entry)
2298 configspec = self._original_configspec
2299 current_options['configspec'] = configspec
2302 self._initialise(current_options)
2303 self._load(filename, configspec)
2307 class SimpleVal(object):
2310 Can be used to check that all members expected are present.
2312 To use it, provide a configspec with all your members in (the value given
2313 will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2314 method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2315 members are present, or a dictionary with True/False meaning
2316 present/missing. (Whole missing sections will be replaced with ``False``)
2320 self.baseErrorClass = ConfigObjError
2322 def check(self, check, member, missing=False):
2323 """A dummy check method, always returns the value unchanged."""
2325 raise self.baseErrorClass()
2329 # Check / processing functions for options
2330 def flatten_errors(cfg, res, levels=None, results=None):
2332 An example function that will turn a nested dictionary of results
2333 (as returned by ``ConfigObj.validate``) into a flat list.
2335 ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2336 dictionary returned by ``validate``.
2338 (This is a recursive function, so you shouldn't use the ``levels`` or
2339 ``results`` arguments - they are used by the function.)
2341 Returns a list of keys that failed. Each member of the list is a tuple :
2345 ([list of sections...], key, result)
2347 If ``validate`` was called with ``preserve_errors=False`` (the default)
2348 then ``result`` will always be ``False``.
2350 *list of sections* is a flattened list of sections that the key was found
2353 If the section was missing (or a section was expected and a scalar provided
2354 - or vice-versa) then key will be ``None``.
2356 If the value (or section) was missing then ``result`` will be ``False``.
2358 If ``validate`` was called with ``preserve_errors=True`` and a value
2359 was present, but failed the check, then ``result`` will be the exception
2360 object returned. You can use this as a string that describes the failure.
2362 For example *The value "3" is of the wrong type*.
2365 >>> vtor = validate.Validator()
2371 ... another_option = Probably
2373 ... another_option = True
2380 ... option1 = boolean()
2381 ... option2 = boolean()
2382 ... option3 = boolean(default=Bad_value)
2384 ... option1 = boolean()
2385 ... option2 = boolean()
2386 ... option3 = boolean(default=Bad_value)
2388 ... another_option = boolean()
2390 ... another_option = boolean()
2393 ... value2 = integer
2394 ... value3 = integer(0, 10)
2395 ... [[[section3b-sub]]]
2398 ... another_option = boolean()
2400 >>> cs = my_cfg.split('\\n')
2401 >>> ini = my_ini.split('\\n')
2402 >>> cfg = ConfigObj(ini, configspec=cs)
2403 >>> res = cfg.validate(vtor, preserve_errors=True)
2405 >>> for entry in flatten_errors(cfg, res):
2406 ... section_list, key, error = entry
2407 ... section_list.insert(0, '[root]')
2408 ... if key is not None:
2409 ... section_list.append(key)
2411 ... section_list.append('[missing]')
2412 ... section_string = ', '.join(section_list)
2413 ... errors.append((section_string, ' = ', error))
2415 >>> for entry in errors:
2416 ... print entry[0], entry[1], (entry[2] or 0)
2418 [root], option3 = the value "Bad_value" is of the wrong type.
2419 [root], section1, option2 = 0
2420 [root], section1, option3 = the value "Bad_value" is of the wrong type.
2421 [root], section2, another_option = the value "Probably" is of the wrong type.
2422 [root], section3, section3b, section3b-sub, [missing] = 0
2423 [root], section3, section3b, value2 = the value "a" is of the wrong type.
2424 [root], section3, section3b, value3 = the value "11" is too big.
2425 [root], section4, [missing] = 0
2433 if res is False or isinstance(res, Exception):
2434 results.append((levels[:], None, res))
2438 for (key, val) in res.items():
2441 if isinstance(cfg.get(key), dict):
2444 flatten_errors(cfg[key], val, levels, results)
2446 results.append((levels[:], key, val))
2455 """*A programming language is a medium of expression.* - Paul Graham"""