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