2 # A config file reader/writer that supports nested sections in config files.
3 # Copyright (C) 2005-2006 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
22 INTP_VER
= sys
.version_info
[:2]
24 raise RuntimeError("Python v.2.2 or later needed")
33 from types
import StringTypes
34 from warnings
import warn
36 from codecs
import BOM_UTF8
, BOM_UTF16
, BOM_UTF16_BE
, BOM_UTF16_LE
38 # Python 2.2 does not have these
40 BOM_UTF8
= '\xef\xbb\xbf'
41 # UTF-16, little endian
42 BOM_UTF16_LE
= '\xff\xfe'
44 BOM_UTF16_BE
= '\xfe\xff'
45 if sys
.byteorder
== 'little':
46 # UTF-16, native endianness
47 BOM_UTF16
= BOM_UTF16_LE
49 # UTF-16, native endianness
50 BOM_UTF16
= BOM_UTF16_BE
52 # A dictionary mapping BOM to
53 # the encoding to decode with, and what to set the
54 # encoding attribute to.
56 BOM_UTF8
: ('utf_8', None),
57 BOM_UTF16_BE
: ('utf16_be', 'utf_16'),
58 BOM_UTF16_LE
: ('utf16_le', 'utf_16'),
59 BOM_UTF16
: ('utf_16', 'utf_16'),
61 # All legal variants of the BOM codecs.
62 # TODO: the list of aliases is not meant to be exhaustive, is there a
69 'utf16_be': 'utf16_be',
70 'utf_16_be': 'utf16_be',
71 'utf-16be': 'utf16_be',
72 'utf16_le': 'utf16_le',
73 'utf_16_le': 'utf16_le',
74 'utf-16le': 'utf16_le',
82 # Map of encodings to the BOM to write.
86 'utf16_be': BOM_UTF16_BE
,
87 'utf16_le': BOM_UTF16_LE
,
92 from validate
import VdtMissingValue
94 VdtMissingValue
= None
100 """enumerate for Python 2.2."""
112 __version__
= '4.4.0'
114 __revision__
= '$Id: configobj.py,v 3.5 2007/07/02 18:20:24 benjamin%smedbergs.us Exp $'
116 __docformat__
= "restructuredtext en"
120 'DEFAULT_INDENT_TYPE',
121 'DEFAULT_INTERPOLATION',
129 'InterpolationError',
130 'InterpolationLoopError',
131 'MissingInterpolationOption',
132 'RepeatSectionError',
139 DEFAULT_INTERPOLATION
= 'configparser'
140 DEFAULT_INDENT_TYPE
= ' '
141 MAX_INTERPOL_DEPTH
= 10
144 'interpolation': True,
145 'raise_errors': False,
147 'create_empty': False,
151 # option may be set to one of ('', ' ', '\t')
154 'default_encoding': None,
156 'write_empty_values': False,
163 raise ImportError('compiler module not available')
164 p
= compiler
.parse(s
)
165 return p
.getChildren()[1].getChildren()[0].getChildren()[1]
167 class UnknownType(Exception):
173 m
= getattr(self
, 'build_' + o
.__class
__.__name
__, None)
175 raise UnknownType(o
.__class
__.__name
__)
178 def build_List(self
, o
):
179 return map(self
.build
, o
.getChildren())
181 def build_Const(self
, o
):
184 def build_Dict(self
, o
):
186 i
= iter(map(self
.build
, o
.getChildren()))
191 def build_Tuple(self
, o
):
192 return tuple(self
.build_List(o
))
194 def build_Name(self
, o
):
199 if o
.name
== 'False':
203 raise UnknownType('Undefined Name')
205 def build_Add(self
, o
):
206 real
, imag
= map(self
.build_Const
, o
.getChildren())
210 raise UnknownType('Add')
211 if not isinstance(imag
, complex) or imag
.real
!= 0.0:
212 raise UnknownType('Add')
215 def build_Getattr(self
, o
):
216 parent
= self
.build(o
.expr
)
217 return getattr(parent
, o
.attrname
)
219 def build_UnarySub(self
, o
):
220 return -self
.build_Const(o
.getChildren()[0])
222 def build_UnaryAdd(self
, o
):
223 return self
.build_Const(o
.getChildren()[0])
228 return Builder().build(getObj(s
))
230 def _splitlines(instring
):
231 """Split a string on lines, without losing line endings or truncating."""
234 class ConfigObjError(SyntaxError):
236 This is the base class for all errors that ConfigObj raises.
237 It is a subclass of SyntaxError.
239 def __init__(self
, message
='', line_number
=None, line
=''):
241 self
.line_number
= line_number
242 self
.message
= message
243 SyntaxError.__init
__(self
, message
)
245 class NestingError(ConfigObjError
):
247 This error indicates a level of nesting that doesn't match.
250 class ParseError(ConfigObjError
):
252 This error indicates that a line is badly written.
253 It is neither a valid ``key = value`` line,
254 nor a valid section marker line.
257 class DuplicateError(ConfigObjError
):
259 The keyword or section specified already exists.
262 class ConfigspecError(ConfigObjError
):
264 An error occured whilst parsing a configspec.
267 class InterpolationError(ConfigObjError
):
268 """Base class for the two interpolation errors."""
270 class InterpolationLoopError(InterpolationError
):
271 """Maximum interpolation depth exceeded in string interpolation."""
273 def __init__(self
, option
):
274 InterpolationError
.__init
__(
276 'interpolation loop detected in value "%s".' % option
)
278 class RepeatSectionError(ConfigObjError
):
280 This error indicates additional sections in a section with a
281 ``__many__`` (repeated) section.
284 class MissingInterpolationOption(InterpolationError
):
285 """A value specified for interpolation was missing."""
287 def __init__(self
, option
):
288 InterpolationError
.__init
__(
290 'missing option "%s" in interpolation.' % option
)
292 class UnreprError(ConfigObjError
):
293 """An error parsing in unrepr mode."""
296 class InterpolationEngine(object):
298 A helper class to help perform string interpolation.
300 This class is an abstract base class; its descendants perform
304 # compiled regexp to use in self.interpolate()
305 _KEYCRE
= re
.compile(r
"%\(([^)]*)\)s")
307 def __init__(self
, section
):
308 # the Section instance that "owns" this engine
309 self
.section
= section
311 def interpolate(self
, key
, value
):
312 def recursive_interpolate(key
, value
, section
, backtrail
):
313 """The function that does the actual work.
315 ``value``: the string we're trying to interpolate.
316 ``section``: the section in which that string was found
317 ``backtrail``: a dict to keep track of where we've been,
318 to detect and prevent infinite recursion loops
320 This is similar to a depth-first-search algorithm.
322 # Have we been here already?
323 if backtrail
.has_key((key
, section
.name
)):
324 # Yes - infinite loop detected
325 raise InterpolationLoopError(key
)
326 # Place a marker on our backtrail so we won't come back here again
327 backtrail
[(key
, section
.name
)] = 1
329 # Now start the actual work
330 match
= self
._KEYCRE
.search(value
)
332 # The actual parsing of the match is implementation-dependent,
333 # so delegate to our helper function
334 k
, v
, s
= self
._parse
_match
(match
)
336 # That's the signal that no further interpolation is needed
339 # Further interpolation may be needed to obtain final value
340 replacement
= recursive_interpolate(k
, v
, s
, backtrail
)
341 # Replace the matched string with its final value
342 start
, end
= match
.span()
343 value
= ''.join((value
[:start
], replacement
, value
[end
:]))
344 new_search_start
= start
+ len(replacement
)
345 # Pick up the next interpolation key, if any, for next time
346 # through the while loop
347 match
= self
._KEYCRE
.search(value
, new_search_start
)
349 # Now safe to come back here again; remove marker from backtrail
350 del backtrail
[(key
, section
.name
)]
354 # Back in interpolate(), all we have to do is kick off the recursive
355 # function with appropriate starting values
356 value
= recursive_interpolate(key
, value
, self
.section
, {})
359 def _fetch(self
, key
):
360 """Helper function to fetch values from owning section.
362 Returns a 2-tuple: the value, and the section where it was found.
364 # switch off interpolation before we try and fetch anything !
365 save_interp
= self
.section
.main
.interpolation
366 self
.section
.main
.interpolation
= False
368 # Start at section that "owns" this InterpolationEngine
369 current_section
= self
.section
371 # try the current section first
372 val
= current_section
.get(key
)
376 val
= current_section
.get('DEFAULT', {}).get(key
)
379 # move up to parent and try again
380 # top-level's parent is itself
381 if current_section
.parent
is current_section
:
382 # reached top level, time to give up
384 current_section
= current_section
.parent
386 # restore interpolation to previous value before returning
387 self
.section
.main
.interpolation
= save_interp
389 raise MissingInterpolationOption(key
)
390 return val
, current_section
392 def _parse_match(self
, match
):
393 """Implementation-dependent helper function.
395 Will be passed a match object corresponding to the interpolation
396 key we just found (e.g., "%(foo)s" or "$foo"). Should look up that
397 key in the appropriate config file section (using the ``_fetch()``
398 helper function) and return a 3-tuple: (key, value, section)
400 ``key`` is the name of the key we're looking for
401 ``value`` is the value found for that key
402 ``section`` is a reference to the section where it was found
404 ``key`` and ``section`` should be None if no further
405 interpolation should be performed on the resulting value
406 (e.g., if we interpolated "$$" and returned "$").
408 raise NotImplementedError
411 class ConfigParserInterpolation(InterpolationEngine
):
412 """Behaves like ConfigParser."""
413 _KEYCRE
= re
.compile(r
"%\(([^)]*)\)s")
415 def _parse_match(self
, match
):
417 value
, section
= self
._fetch
(key
)
418 return key
, value
, section
421 class TemplateInterpolation(InterpolationEngine
):
422 """Behaves like string.Template."""
424 _KEYCRE
= re
.compile(r
"""
426 (?P<escaped>\$) | # Two $ signs
427 (?P<named>[_a-z][_a-z0-9]*) | # $name format
428 {(?P<braced>[^}]*)} # ${name} format
430 """, re
.IGNORECASE | re
.VERBOSE
)
432 def _parse_match(self
, match
):
433 # Valid name (in or out of braces): fetch value from section
434 key
= match
.group('named') or match
.group('braced')
436 value
, section
= self
._fetch
(key
)
437 return key
, value
, section
438 # Escaped delimiter (e.g., $$): return single delimiter
439 if match
.group('escaped') is not None:
440 # Return None for key and section to indicate it's time to stop
441 return None, self
._delimiter
, None
442 # Anything else: ignore completely, just return it unchanged
443 return None, match
.group(), None
445 interpolation_engines
= {
446 'configparser': ConfigParserInterpolation
,
447 'template': TemplateInterpolation
,
452 A dictionary-like object that represents a section in a config file.
454 It does string interpolation if the 'interpolation' attribute
455 of the 'main' object is set to True.
457 Interpolation is tried first from this object, then from the 'DEFAULT'
458 section of this object, next from the parent and its 'DEFAULT' section,
459 and so on until the main object is reached.
461 A Section will behave like an ordered dictionary - following the
462 order of the ``scalars`` and ``sections`` attributes.
463 You can use this to change the order of members.
465 Iteration follows the order: scalars, then sections.
468 def __init__(self
, parent
, depth
, main
, indict
=None, name
=None):
470 * parent is the section above
471 * depth is the depth level of this section
472 * main is the main ConfigObj
473 * indict is a dictionary to initialise the section with
478 # used for nesting level *and* interpolation
480 # used for the interpolation attribute
482 # level of nesting depth of this Section
484 # the sequence of scalar values in this Section
486 # the sequence of sections in this Section
488 # purely for information
492 self
.inline_comments
= {}
496 self
._configspec
_comments
= {}
497 self
._configspec
_inline
_comments
= {}
498 self
._cs
_section
_comments
= {}
499 self
._cs
_section
_inline
_comments
= {}
503 # we do this explicitly so that __setitem__ is used properly
504 # (rather than just passing to ``dict.__init__``)
506 self
[entry
] = indict
[entry
]
508 def _interpolate(self
, key
, value
):
510 # do we already have an interpolation engine?
511 engine
= self
._interpolation
_engine
512 except AttributeError:
513 # not yet: first time running _interpolate(), so pick the engine
514 name
= self
.main
.interpolation
515 if name
== True: # note that "if name:" would be incorrect here
516 # backwards-compatibility: interpolation=True means use default
517 name
= DEFAULT_INTERPOLATION
518 name
= name
.lower() # so that "Template", "template", etc. all work
519 class_
= interpolation_engines
.get(name
, None)
521 # invalid value for self.main.interpolation
522 self
.main
.interpolation
= False
525 # save reference to engine so we don't have to do this again
526 engine
= self
._interpolation
_engine
= class_(self
)
527 # let the engine do the actual work
528 return engine
.interpolate(key
, value
)
530 def __getitem__(self
, key
):
531 """Fetch the item and do string interpolation."""
532 val
= dict.__getitem
__(self
, key
)
533 if self
.main
.interpolation
and isinstance(val
, StringTypes
):
534 return self
._interpolate
(key
, val
)
537 def __setitem__(self
, key
, value
, unrepr
=False):
539 Correctly set a value.
541 Making dictionary values Section instances.
542 (We have to special case 'Section' instances - which are also dicts)
544 Keys must be strings.
545 Values need only be strings (or lists of strings) if
546 ``main.stringify`` is set.
548 `unrepr`` must be set when setting a value to a dictionary, without
549 creating a new sub-section.
551 if not isinstance(key
, StringTypes
):
552 raise ValueError, 'The key "%s" is not a string.' % key
554 if not self
.comments
.has_key(key
):
555 self
.comments
[key
] = []
556 self
.inline_comments
[key
] = ''
557 # remove the entry from defaults
558 if key
in self
.defaults
:
559 self
.defaults
.remove(key
)
561 if isinstance(value
, Section
):
562 if not self
.has_key(key
):
563 self
.sections
.append(key
)
564 dict.__setitem
__(self
, key
, value
)
565 elif isinstance(value
, dict) and not unrepr
:
566 # First create the new depth level,
567 # then create the section
568 if not self
.has_key(key
):
569 self
.sections
.append(key
)
570 new_depth
= self
.depth
+ 1
581 if not self
.has_key(key
):
582 self
.scalars
.append(key
)
583 if not self
.main
.stringify
:
584 if isinstance(value
, StringTypes
):
586 elif isinstance(value
, (list, tuple)):
588 if not isinstance(entry
, StringTypes
):
590 'Value is not a string "%s".' % entry
)
592 raise TypeError, 'Value is not a string "%s".' % value
593 dict.__setitem
__(self
, key
, value
)
595 def __delitem__(self
, key
):
596 """Remove items from the sequence when deleting."""
597 dict. __delitem__(self
, key
)
598 if key
in self
.scalars
:
599 self
.scalars
.remove(key
)
601 self
.sections
.remove(key
)
602 del self
.comments
[key
]
603 del self
.inline_comments
[key
]
605 def get(self
, key
, default
=None):
606 """A version of ``get`` that doesn't bypass string interpolation."""
612 def update(self
, indict
):
614 A version of update that uses our ``__setitem__``.
617 self
[entry
] = indict
[entry
]
619 def pop(self
, key
, *args
):
621 val
= dict.pop(self
, key
, *args
)
622 if key
in self
.scalars
:
623 del self
.comments
[key
]
624 del self
.inline_comments
[key
]
625 self
.scalars
.remove(key
)
626 elif key
in self
.sections
:
627 del self
.comments
[key
]
628 del self
.inline_comments
[key
]
629 self
.sections
.remove(key
)
630 if self
.main
.interpolation
and isinstance(val
, StringTypes
):
631 return self
._interpolate
(key
, val
)
635 """Pops the first (key,val)"""
636 sequence
= (self
.scalars
+ self
.sections
)
638 raise KeyError, ": 'popitem(): dictionary is empty'"
646 A version of clear that also affects scalars/sections
647 Also clears comments and configspec.
649 Leaves other attributes alone :
650 depth/main/parent are not affected
656 self
.inline_comments
= {}
659 def setdefault(self
, key
, default
=None):
660 """A version of setdefault that sets sequence if appropriate."""
669 return zip((self
.scalars
+ self
.sections
), self
.values())
673 return (self
.scalars
+ self
.sections
)
677 return [self
[key
] for key
in (self
.scalars
+ self
.sections
)]
681 return iter(self
.items())
685 return iter((self
.scalars
+ self
.sections
))
689 def itervalues(self
):
691 return iter(self
.values())
694 return '{%s}' % ', '.join([('%s: %s' % (repr(key
), repr(self
[key
])))
695 for key
in (self
.scalars
+ self
.sections
)])
699 # Extra methods - not in a normal dictionary
703 Return a deepcopy of self as a dictionary.
705 All members that are ``Section`` instances are recursively turned to
706 ordinary dictionaries - by calling their ``dict`` method.
716 this_entry
= self
[entry
]
717 if isinstance(this_entry
, Section
):
718 this_entry
= this_entry
.dict()
719 elif isinstance(this_entry
, list):
720 # create a copy rather than a reference
721 this_entry
= list(this_entry
)
722 elif isinstance(this_entry
, tuple):
723 # create a copy rather than a reference
724 this_entry
= tuple(this_entry
)
725 newdict
[entry
] = this_entry
728 def merge(self
, indict
):
730 A recursive update - useful for merging config files.
732 >>> a = '''[section1]
735 ... more_options = False
736 ... # end of file'''.splitlines()
737 >>> b = '''# File is user.ini
740 ... # end of file'''.splitlines()
741 >>> c1 = ConfigObj(b)
742 >>> c2 = ConfigObj(a)
745 {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
747 for key
, val
in indict
.items():
748 if (key
in self
and isinstance(self
[key
], dict) and
749 isinstance(val
, dict)):
754 def rename(self
, oldkey
, newkey
):
756 Change a keyname to another, without changing position in sequence.
758 Implemented so that transformations can be made on keys,
759 as well as on values. (used by encode and decode)
761 Also renames comments.
763 if oldkey
in self
.scalars
:
764 the_list
= self
.scalars
765 elif oldkey
in self
.sections
:
766 the_list
= self
.sections
768 raise KeyError, 'Key "%s" not found.' % oldkey
769 pos
= the_list
.index(oldkey
)
772 dict.__delitem
__(self
, oldkey
)
773 dict.__setitem
__(self
, newkey
, val
)
774 the_list
.remove(oldkey
)
775 the_list
.insert(pos
, newkey
)
776 comm
= self
.comments
[oldkey
]
777 inline_comment
= self
.inline_comments
[oldkey
]
778 del self
.comments
[oldkey
]
779 del self
.inline_comments
[oldkey
]
780 self
.comments
[newkey
] = comm
781 self
.inline_comments
[newkey
] = inline_comment
783 def walk(self
, function
, raise_errors
=True,
784 call_on_sections
=False, **keywargs
):
786 Walk every member and call a function on the keyword and value.
788 Return a dictionary of the return values
790 If the function raises an exception, raise the errror
791 unless ``raise_errors=False``, in which case set the return value to
794 Any unrecognised keyword arguments you pass to walk, will be pased on
795 to the function you pass in.
797 Note: if ``call_on_sections`` is ``True`` then - on encountering a
798 subsection, *first* the function is called for the *whole* subsection,
799 and then recurses into it's members. This means your function must be
800 able to handle strings, dictionaries and lists. This allows you
801 to change the key of subsections as well as for ordinary members. The
802 return value when called on the whole subsection has to be discarded.
804 See the encode and decode methods for examples, including functions.
808 You can use ``walk`` to transform the names of members of a section
809 but you mustn't add or delete members.
811 >>> config = '''[XXXXsection]
812 ... XXXXkey = XXXXvalue'''.splitlines()
813 >>> cfg = ConfigObj(config)
815 {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
816 >>> def transform(section, key):
817 ... val = section[key]
818 ... newkey = key.replace('XXXX', 'CLIENT1')
819 ... section.rename(key, newkey)
820 ... if isinstance(val, (tuple, list, dict)):
823 ... val = val.replace('XXXX', 'CLIENT1')
824 ... section[newkey] = val
825 >>> cfg.walk(transform, call_on_sections=True)
826 {'CLIENT1section': {'CLIENT1key': None}}
828 {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
832 for i
in range(len(self
.scalars
)):
833 entry
= self
.scalars
[i
]
835 val
= function(self
, entry
, **keywargs
)
836 # bound again in case name has changed
837 entry
= self
.scalars
[i
]
843 entry
= self
.scalars
[i
]
846 for i
in range(len(self
.sections
)):
847 entry
= self
.sections
[i
]
850 function(self
, entry
, **keywargs
)
855 entry
= self
.sections
[i
]
857 # bound again in case name has changed
858 entry
= self
.sections
[i
]
859 # previous result is discarded
860 out
[entry
] = self
[entry
].walk(
862 raise_errors
=raise_errors
,
863 call_on_sections
=call_on_sections
,
867 def decode(self
, encoding
):
869 Decode all strings and values to unicode, using the specified encoding.
871 Works with subsections and list values.
873 Uses the ``walk`` method.
875 Testing ``encode`` and ``decode``.
877 >>> m.decode('ascii')
878 >>> def testuni(val):
879 ... for entry in val:
880 ... if not isinstance(entry, unicode):
881 ... print >> sys.stderr, type(entry)
882 ... raise AssertionError, 'decode failed.'
883 ... if isinstance(val[entry], dict):
884 ... testuni(val[entry])
885 ... elif not isinstance(val[entry], unicode):
886 ... raise AssertionError, 'decode failed.'
888 >>> m.encode('ascii')
892 warn('use of ``decode`` is deprecated.', DeprecationWarning)
893 def decode(section
, key
, encoding
=encoding
, warn
=True):
896 if isinstance(val
, (list, tuple)):
899 newval
.append(entry
.decode(encoding
))
900 elif isinstance(val
, dict):
903 newval
= val
.decode(encoding
)
904 newkey
= key
.decode(encoding
)
905 section
.rename(key
, newkey
)
906 section
[newkey
] = newval
907 # using ``call_on_sections`` allows us to modify section names
908 self
.walk(decode
, call_on_sections
=True)
910 def encode(self
, encoding
):
912 Encode all strings and values from unicode,
913 using the specified encoding.
915 Works with subsections and list values.
916 Uses the ``walk`` method.
918 warn('use of ``encode`` is deprecated.', DeprecationWarning)
919 def encode(section
, key
, encoding
=encoding
):
922 if isinstance(val
, (list, tuple)):
925 newval
.append(entry
.encode(encoding
))
926 elif isinstance(val
, dict):
929 newval
= val
.encode(encoding
)
930 newkey
= key
.encode(encoding
)
931 section
.rename(key
, newkey
)
932 section
[newkey
] = newval
933 self
.walk(encode
, call_on_sections
=True)
935 def istrue(self
, key
):
936 """A deprecated version of ``as_bool``."""
937 warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
938 'instead.', DeprecationWarning)
939 return self
.as_bool(key
)
941 def as_bool(self
, key
):
943 Accepts a key as input. The corresponding value must be a string or
944 the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
945 retain compatibility with Python 2.2.
947 If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
950 If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
953 ``as_bool`` is not case sensitive.
955 Any other input will raise a ``ValueError``.
960 Traceback (most recent call last):
961 ValueError: Value "fish" is neither True nor False
976 if not isinstance(val
, StringTypes
):
979 return self
.main
._bools
[val
.lower()]
981 raise ValueError('Value "%s" is neither True nor False' % val
)
983 def as_int(self
, key
):
985 A convenience method which coerces the specified value to an integer.
987 If the value is an invalid literal for ``int``, a ``ValueError`` will
993 Traceback (most recent call last):
994 ValueError: invalid literal for int(): fish
1000 Traceback (most recent call last):
1001 ValueError: invalid literal for int(): 3.2
1003 return int(self
[key
])
1005 def as_float(self
, key
):
1007 A convenience method which coerces the specified value to a float.
1009 If the value is an invalid literal for ``float``, a ``ValueError`` will
1015 Traceback (most recent call last):
1016 ValueError: invalid literal for float(): fish
1024 return float(self
[key
])
1027 class ConfigObj(Section
):
1028 """An object to read, create, and write config files."""
1030 _keyword
= re
.compile(r
'''^ # line start
1033 (?:".*?")| # double quotes
1034 (?:'.*?')| # single quotes
1035 (?:[^'"=].*?) # no quotes
1038 (.*) # value (including list values and comments)
1043 _sectionmarker
= re
.compile(r
'''^
1044 (\s*) # 1: indentation
1045 ((?:\[\s*)+) # 2: section marker open
1046 ( # 3: section name open
1047 (?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1048 (?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1049 (?:[^'"\s].*?) # at least one non-space unquoted
1050 ) # section name close
1051 ((?:\s*\])+) # 4: section marker close
1052 \s*(\#.*)? # 5: optional comment
1056 # this regexp pulls list values out as a single string
1057 # or single values and comments
1058 # FIXME: this regex adds a '' to the end of comma terminated lists
1059 # workaround in ``_handle_value``
1060 _valueexp
= re
.compile(r
'''^
1066 (?:".*?")| # double quotes
1067 (?:'.*?')| # single quotes
1068 (?:[^'",\#][^,\#]*?) # unquoted
1071 )* # match all list items ending in a comma (if any)
1074 (?:".*?")| # double quotes
1075 (?:'.*?')| # single quotes
1076 (?:[^'",\#\s][^,]*?)| # unquoted
1077 (?:(?<!,)) # Empty value
1078 )? # last item in a list - or string value
1080 (,) # alternatively a single comma - empty list
1082 \s*(\#.*)? # optional comment
1086 # use findall to get the members of a list value
1087 _listvalueexp
= re
.compile(r
'''
1089 (?:".*?")| # double quotes
1090 (?:'.*?')| # single quotes
1091 (?:[^'",\#].*?) # unquoted
1097 # this regexp is used for the value
1098 # when lists are switched off
1099 _nolistvalue
= re
.compile(r
'''^
1101 (?:".*?")| # double quotes
1102 (?:'.*?')| # single quotes
1103 (?:[^'"\#].*?)| # unquoted
1106 \s*(\#.*)? # optional comment
1110 # regexes for finding triple quoted values on one line
1111 _single_line_single
= re
.compile(r
"^'''(.*?)'''\s*(#.*)?$")
1112 _single_line_double
= re
.compile(r
'^"""(.*?)"""\s*(#.*)?$')
1113 _multi_line_single
= re
.compile(r
"^(.*?)'''\s*(#.*)?$")
1114 _multi_line_double
= re
.compile(r
'^(.*?)"""\s*(#.*)?$')
1117 "'''": (_single_line_single
, _multi_line_single
),
1118 '"""': (_single_line_double
, _multi_line_double
),
1121 # Used by the ``istrue`` Section method
1123 'yes': True, 'no': False,
1124 'on': True, 'off': False,
1125 '1': True, '0': False,
1126 'true': True, 'false': False,
1129 def __init__(self
, infile
=None, options
=None, **kwargs
):
1131 Parse or create a config file object.
1133 ``ConfigObj(infile=None, options=None, **kwargs)``
1140 options
= dict(options
)
1141 # keyword arguments take precedence over an options dictionary
1142 options
.update(kwargs
)
1143 # init the superclass
1144 Section
.__init
__(self
, self
, 0, self
)
1146 defaults
= OPTION_DEFAULTS
.copy()
1147 for entry
in options
.keys():
1148 if entry
not in defaults
.keys():
1149 raise TypeError, 'Unrecognised option "%s".' % entry
1150 # TODO: check the values too.
1152 # Add any explicit options to the defaults
1153 defaults
.update(options
)
1155 # initialise a few variables
1156 self
.filename
= None
1158 self
.raise_errors
= defaults
['raise_errors']
1159 self
.interpolation
= defaults
['interpolation']
1160 self
.list_values
= defaults
['list_values']
1161 self
.create_empty
= defaults
['create_empty']
1162 self
.file_error
= defaults
['file_error']
1163 self
.stringify
= defaults
['stringify']
1164 self
.indent_type
= defaults
['indent_type']
1165 self
.encoding
= defaults
['encoding']
1166 self
.default_encoding
= defaults
['default_encoding']
1168 self
.newlines
= None
1169 self
.write_empty_values
= defaults
['write_empty_values']
1170 self
.unrepr
= defaults
['unrepr']
1172 self
.initial_comment
= []
1173 self
.final_comment
= []
1175 self
._terminated
= False
1177 if isinstance(infile
, StringTypes
):
1178 self
.filename
= infile
1179 if os
.path
.isfile(infile
):
1180 infile
= open(infile
).read() or []
1181 elif self
.file_error
:
1182 # raise an error if the file doesn't exist
1183 raise IOError, 'Config file not found: "%s".' % self
.filename
1185 # file doesn't already exist
1186 if self
.create_empty
:
1187 # this is a good test that the filename specified
1188 # isn't impossible - like on a non existent device
1189 h
= open(infile
, 'w')
1193 elif isinstance(infile
, (list, tuple)):
1194 infile
= list(infile
)
1195 elif isinstance(infile
, dict):
1197 # the Section class handles creating subsections
1198 if isinstance(infile
, ConfigObj
):
1199 # get a copy of our ConfigObj
1200 infile
= infile
.dict()
1201 for entry
in infile
:
1202 self
[entry
] = infile
[entry
]
1204 if defaults
['configspec'] is not None:
1205 self
._handle
_configspec
(defaults
['configspec'])
1207 self
.configspec
= None
1209 elif hasattr(infile
, 'read'):
1210 # This supports file like objects
1211 infile
= infile
.read() or []
1212 # needs splitting into lines - but needs doing *after* decoding
1213 # in case it's not an 8 bit encoding
1215 raise TypeError, ('infile must be a filename,'
1216 ' file like object, or list of lines.')
1219 # don't do it for the empty ConfigObj
1220 infile
= self
._handle
_bom
(infile
)
1221 # infile is now *always* a list
1223 # Set the newlines attribute (first line ending it finds)
1224 # and strip trailing '\n' or '\r' from lines
1226 if (not line
) or (line
[-1] not in ('\r', '\n', '\r\n')):
1228 for end
in ('\r\n', '\n', '\r'):
1229 if line
.endswith(end
):
1233 if infile
[-1] and infile
[-1] in ('\r', '\n', '\r\n'):
1234 self
._terminated
= True
1235 infile
= [line
.rstrip('\r\n') for line
in infile
]
1238 # if we had any errors, now is the time to raise them
1240 info
= "at line %s." % self
._errors
[0].line_number
1241 if len(self
._errors
) > 1:
1242 msg
= ("Parsing failed with several errors.\nFirst error %s" %
1244 error
= ConfigObjError(msg
)
1246 error
= self
._errors
[0]
1247 # set the errors attribute; it's a list of tuples:
1248 # (error_type, message, line_number)
1249 error
.errors
= self
._errors
1250 # set the config attribute
1253 # delete private attributes
1256 if defaults
['configspec'] is None:
1257 self
.configspec
= None
1259 self
._handle
_configspec
(defaults
['configspec'])
1262 return 'ConfigObj({%s})' % ', '.join(
1263 [('%s: %s' % (repr(key
), repr(self
[key
]))) for key
in
1264 (self
.scalars
+ self
.sections
)])
1266 def _handle_bom(self
, infile
):
1268 Handle any BOM, and decode if necessary.
1270 If an encoding is specified, that *must* be used - but the BOM should
1271 still be removed (and the BOM attribute set).
1273 (If the encoding is wrongly specified, then a BOM for an alternative
1274 encoding won't be discovered or removed.)
1276 If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1277 removed. The BOM attribute will be set. UTF16 will be decoded to
1280 NOTE: This method must not be called with an empty ``infile``.
1282 Specifying the *wrong* encoding is likely to cause a
1283 ``UnicodeDecodeError``.
1285 ``infile`` must always be returned as a list of lines, but may be
1286 passed in as a single string.
1288 if ((self
.encoding
is not None) and
1289 (self
.encoding
.lower() not in BOM_LIST
)):
1290 # No need to check for a BOM
1291 # the encoding specified doesn't have one
1293 return self
._decode
(infile
, self
.encoding
)
1295 if isinstance(infile
, (list, tuple)):
1299 if self
.encoding
is not None:
1300 # encoding explicitly supplied
1301 # And it could have an associated BOM
1302 # TODO: if encoding is just UTF16 - we ought to check for both
1303 # TODO: big endian and little endian versions.
1304 enc
= BOM_LIST
[self
.encoding
.lower()]
1306 # For UTF16 we try big endian and little endian
1307 for BOM
, (encoding
, final_encoding
) in BOMS
.items():
1308 if not final_encoding
:
1311 if infile
.startswith(BOM
):
1314 # Don't need to remove BOM
1315 return self
._decode
(infile
, encoding
)
1317 # If we get this far, will *probably* raise a DecodeError
1318 # As it doesn't appear to start with a BOM
1319 return self
._decode
(infile
, self
.encoding
)
1323 if not line
.startswith(BOM
):
1324 return self
._decode
(infile
, self
.encoding
)
1326 newline
= line
[len(BOM
):]
1329 if isinstance(infile
, (list, tuple)):
1334 return self
._decode
(infile
, self
.encoding
)
1336 # No encoding specified - so we need to check for UTF8/UTF16
1337 for BOM
, (encoding
, final_encoding
) in BOMS
.items():
1338 if not line
.startswith(BOM
):
1342 self
.encoding
= final_encoding
1343 if not final_encoding
:
1347 newline
= line
[len(BOM
):]
1348 if isinstance(infile
, (list, tuple)):
1352 # UTF8 - don't decode
1353 if isinstance(infile
, StringTypes
):
1354 return infile
.splitlines(True)
1357 # UTF16 - have to decode
1358 return self
._decode
(infile
, encoding
)
1360 # No BOM discovered and no encoding specified, just return
1361 if isinstance(infile
, StringTypes
):
1362 # infile read from a file will be a single string
1363 return infile
.splitlines(True)
1367 def _a_to_u(self
, aString
):
1368 """Decode ASCII strings to unicode if a self.encoding is specified."""
1370 return aString
.decode('ascii')
1374 def _decode(self
, infile
, encoding
):
1376 Decode infile to unicode. Using the specified encoding.
1378 if is a string, it also needs converting to a list.
1380 if isinstance(infile
, StringTypes
):
1382 # NOTE: Could raise a ``UnicodeDecodeError``
1383 return infile
.decode(encoding
).splitlines(True)
1384 for i
, line
in enumerate(infile
):
1385 if not isinstance(line
, unicode):
1386 # NOTE: The isinstance test here handles mixed lists of unicode/string
1387 # NOTE: But the decode will break on any non-string values
1388 # NOTE: Or could raise a ``UnicodeDecodeError``
1389 infile
[i
] = line
.decode(encoding
)
1392 def _decode_element(self
, line
):
1393 """Decode element to unicode if necessary."""
1394 if not self
.encoding
:
1396 if isinstance(line
, str) and self
.default_encoding
:
1397 return line
.decode(self
.default_encoding
)
1400 def _str(self
, value
):
1402 Used by ``stringify`` within validate, to turn non-string values
1405 if not isinstance(value
, StringTypes
):
1410 def _parse(self
, infile
):
1411 """Actually parse the config file."""
1412 temp_list_values
= self
.list_values
1414 self
.list_values
= False
1418 maxline
= len(infile
) - 1
1420 reset_comment
= False
1421 while cur_index
< maxline
:
1425 line
= infile
[cur_index
]
1426 sline
= line
.strip()
1427 # do we have anything on the line ?
1428 if not sline
or sline
.startswith('#') or sline
.startswith(';'):
1429 reset_comment
= False
1430 comment_list
.append(line
)
1433 # preserve initial comment
1434 self
.initial_comment
= comment_list
1437 reset_comment
= True
1438 # first we check if it's a section marker
1439 mat
= self
._sectionmarker
.match(line
)
1442 (indent
, sect_open
, sect_name
, sect_close
, comment
) = (
1444 if indent
and (self
.indent_type
is None):
1445 self
.indent_type
= indent
1446 cur_depth
= sect_open
.count('[')
1447 if cur_depth
!= sect_close
.count(']'):
1449 "Cannot compute the section depth at line %s.",
1450 NestingError
, infile
, cur_index
)
1453 if cur_depth
< this_section
.depth
:
1454 # the new section is dropping back to a previous level
1456 parent
= self
._match
_depth
(
1461 "Cannot compute nesting level at line %s.",
1462 NestingError
, infile
, cur_index
)
1464 elif cur_depth
== this_section
.depth
:
1465 # the new section is a sibling of the current section
1466 parent
= this_section
.parent
1467 elif cur_depth
== this_section
.depth
+ 1:
1468 # the new section is a child the current section
1469 parent
= this_section
1472 "Section too nested at line %s.",
1473 NestingError
, infile
, cur_index
)
1475 sect_name
= self
._unquote
(sect_name
)
1476 if parent
.has_key(sect_name
):
1478 'Duplicate section name at line %s.',
1479 DuplicateError
, infile
, cur_index
)
1481 # create the new section
1482 this_section
= Section(
1487 parent
[sect_name
] = this_section
1488 parent
.inline_comments
[sect_name
] = comment
1489 parent
.comments
[sect_name
] = comment_list
1492 # it's not a section marker,
1493 # so it should be a valid ``key = value`` line
1494 mat
= self
._keyword
.match(line
)
1496 # it neither matched as a keyword
1497 # or a section marker
1499 'Invalid line at line "%s".',
1500 ParseError
, infile
, cur_index
)
1502 # is a keyword value
1503 # value will include any inline comment
1504 (indent
, key
, value
) = mat
.groups()
1505 if indent
and (self
.indent_type
is None):
1506 self
.indent_type
= indent
1507 # check for a multiline value
1508 if value
[:3] in ['"""', "'''"]:
1510 (value
, comment
, cur_index
) = self
._multiline
(
1511 value
, infile
, cur_index
, maxline
)
1514 'Parse error in value at line %s.',
1515 ParseError
, infile
, cur_index
)
1521 value
= unrepr(value
)
1522 except Exception, e
:
1523 if type(e
) == UnknownType
:
1524 msg
= 'Unknown name or type in value at line %s.'
1526 msg
= 'Parse error in value at line %s.'
1527 self
._handle
_error
(msg
, UnreprError
, infile
,
1534 value
= unrepr(value
)
1535 except Exception, e
:
1536 if isinstance(e
, UnknownType
):
1537 msg
= 'Unknown name or type in value at line %s.'
1539 msg
= 'Parse error in value at line %s.'
1540 self
._handle
_error
(msg
, UnreprError
, infile
,
1544 # extract comment and lists
1546 (value
, comment
) = self
._handle
_value
(value
)
1549 'Parse error in value at line %s.',
1550 ParseError
, infile
, cur_index
)
1553 key
= self
._unquote
(key
)
1554 if this_section
.has_key(key
):
1556 'Duplicate keyword name at line %s.',
1557 DuplicateError
, infile
, cur_index
)
1560 # we set unrepr because if we have got this far we will never
1561 # be creating a new section
1562 this_section
.__setitem
__(key
, value
, unrepr
=True)
1563 this_section
.inline_comments
[key
] = comment
1564 this_section
.comments
[key
] = comment_list
1567 if self
.indent_type
is None:
1568 # no indentation used, set the type accordingly
1569 self
.indent_type
= ''
1571 if self
._terminated
:
1572 comment_list
.append('')
1573 # preserve the final comment
1574 if not self
and not self
.initial_comment
:
1575 self
.initial_comment
= comment_list
1576 elif not reset_comment
:
1577 self
.final_comment
= comment_list
1578 self
.list_values
= temp_list_values
1580 def _match_depth(self
, sect
, depth
):
1582 Given a section and a depth level, walk back through the sections
1583 parents to see if the depth level matches a previous section.
1585 Return a reference to the right section,
1586 or raise a SyntaxError.
1588 while depth
< sect
.depth
:
1589 if sect
is sect
.parent
:
1590 # we've reached the top level already
1593 if sect
.depth
== depth
:
1595 # shouldn't get here
1598 def _handle_error(self
, text
, ErrorClass
, infile
, cur_index
):
1600 Handle an error according to the error settings.
1602 Either raise the error or store it.
1603 The error will have occured at ``cur_index``
1605 line
= infile
[cur_index
]
1607 message
= text
% cur_index
1608 error
= ErrorClass(message
, cur_index
, line
)
1609 if self
.raise_errors
:
1610 # raise the error - parsing stops here
1613 # reraise when parsing has finished
1614 self
._errors
.append(error
)
1616 def _unquote(self
, value
):
1617 """Return an unquoted version of a value"""
1618 if (value
[0] == value
[-1]) and (value
[0] in ('"', "'")):
1622 def _quote(self
, value
, multiline
=True):
1624 Return a safely quoted version of a value.
1626 Raise a ConfigObjError if the value cannot be safely quoted.
1627 If multiline is ``True`` (default) then use triple quotes
1630 Don't quote values that don't need it.
1631 Recursively quote members of a list and return a comma joined list.
1632 Multiline is ``False`` for lists.
1633 Obey list syntax for empty and single member lists.
1635 If ``list_values=False`` then the value is only quoted if it contains
1636 a ``\n`` (is multiline).
1638 If ``write_empty_values`` is set, and the value is an empty string, it
1641 if multiline
and self
.write_empty_values
and value
== '':
1642 # Only if multiline is set, so that it is used for values not
1643 # keys, and not values that are part of a list
1645 if multiline
and isinstance(value
, (list, tuple)):
1648 elif len(value
) == 1:
1649 return self
._quote
(value
[0], multiline
=False) + ','
1650 return ', '.join([self
._quote
(val
, multiline
=False)
1652 if not isinstance(value
, StringTypes
):
1656 raise TypeError, 'Value "%s" is not a string.' % value
1660 wspace_plus
= ' \r\t\n\v\t\'"'
1665 if (not self
.list_values
and '\n' not in value
) or not (multiline
and
1666 ((("'" in value
) and ('"' in value
)) or ('\n' in value
))):
1667 if not self
.list_values
:
1668 # we don't quote if ``list_values=False``
1670 # for normal values either single or double quotes will do
1672 # will only happen if multiline is off - e.g. '\n' in key
1673 raise ConfigObjError
, ('Value "%s" cannot be safely quoted.' %
1675 elif ((value
[0] not in wspace_plus
) and
1676 (value
[-1] not in wspace_plus
) and
1677 (',' not in value
)):
1680 if ("'" in value
) and ('"' in value
):
1681 raise ConfigObjError
, (
1682 'Value "%s" cannot be safely quoted.' % value
)
1688 # if value has '\n' or "'" *and* '"', it will need triple quotes
1689 if (value
.find('"""') != -1) and (value
.find("'''") != -1):
1690 raise ConfigObjError
, (
1691 'Value "%s" cannot be safely quoted.' % value
)
1692 if value
.find('"""') == -1:
1698 def _handle_value(self
, value
):
1700 Given a value string, unquote, remove comment,
1701 handle lists. (including empty and single member lists)
1703 # do we look for lists in values ?
1704 if not self
.list_values
:
1705 mat
= self
._nolistvalue
.match(value
)
1708 # NOTE: we don't unquote here
1711 mat
= self
._valueexp
.match(value
)
1713 # the value is badly constructed, probably badly quoted,
1714 # or an invalid list
1716 (list_values
, single
, empty_list
, comment
) = mat
.groups()
1717 if (list_values
== '') and (single
is None):
1718 # change this if you want to accept empty values
1720 # NOTE: note there is no error handling from here if the regex
1721 # is wrong: then incorrect values will slip through
1722 if empty_list
is not None:
1723 # the single comma - meaning an empty list
1724 return ([], comment
)
1725 if single
is not None:
1726 # handle empty values
1727 if list_values
and not single
:
1728 # FIXME: the '' is a workaround because our regex now matches
1729 # '' at the end of a list if it has a trailing comma
1732 single
= single
or '""'
1733 single
= self
._unquote
(single
)
1734 if list_values
== '':
1736 return (single
, comment
)
1737 the_list
= self
._listvalueexp
.findall(list_values
)
1738 the_list
= [self
._unquote
(val
) for val
in the_list
]
1739 if single
is not None:
1740 the_list
+= [single
]
1741 return (the_list
, comment
)
1743 def _multiline(self
, value
, infile
, cur_index
, maxline
):
1744 """Extract the value, where we are in a multiline situation."""
1746 newvalue
= value
[3:]
1747 single_line
= self
._triple
_quote
[quot
][0]
1748 multi_line
= self
._triple
_quote
[quot
][1]
1749 mat
= single_line
.match(value
)
1751 retval
= list(mat
.groups())
1752 retval
.append(cur_index
)
1754 elif newvalue
.find(quot
) != -1:
1755 # somehow the triple quote is missing
1758 while cur_index
< maxline
:
1761 line
= infile
[cur_index
]
1762 if line
.find(quot
) == -1:
1765 # end of multiline, process it
1768 # we've got to the end of the config, oops...
1770 mat
= multi_line
.match(line
)
1772 # a badly formed line
1774 (value
, comment
) = mat
.groups()
1775 return (newvalue
+ value
, comment
, cur_index
)
1777 def _handle_configspec(self
, configspec
):
1778 """Parse the configspec."""
1779 # FIXME: Should we check that the configspec was created with the
1780 # correct settings ? (i.e. ``list_values=False``)
1781 if not isinstance(configspec
, ConfigObj
):
1783 configspec
= ConfigObj(
1788 except ConfigObjError
, e
:
1789 # FIXME: Should these errors have a reference
1790 # to the already parsed ConfigObj ?
1791 raise ConfigspecError('Parsing configspec failed: %s' % e
)
1793 raise IOError('Reading configspec failed: %s' % e
)
1794 self
._set
_configspec
_value
(configspec
, self
)
1796 def _set_configspec_value(self
, configspec
, section
):
1797 """Used to recursively set configspec values."""
1798 if '__many__' in configspec
.sections
:
1799 section
.configspec
['__many__'] = configspec
['__many__']
1800 if len(configspec
.sections
) > 1:
1801 # FIXME: can we supply any useful information here ?
1802 raise RepeatSectionError
1803 if hasattr(configspec
, 'initial_comment'):
1804 section
._configspec
_initial
_comment
= configspec
.initial_comment
1805 section
._configspec
_final
_comment
= configspec
.final_comment
1806 section
._configspec
_encoding
= configspec
.encoding
1807 section
._configspec
_BOM
= configspec
.BOM
1808 section
._configspec
_newlines
= configspec
.newlines
1809 section
._configspec
_indent
_type
= configspec
.indent_type
1810 for entry
in configspec
.scalars
:
1811 section
._configspec
_comments
[entry
] = configspec
.comments
[entry
]
1812 section
._configspec
_inline
_comments
[entry
] = (
1813 configspec
.inline_comments
[entry
])
1814 section
.configspec
[entry
] = configspec
[entry
]
1815 section
._order
.append(entry
)
1816 for entry
in configspec
.sections
:
1817 if entry
== '__many__':
1819 section
._cs
_section
_comments
[entry
] = configspec
.comments
[entry
]
1820 section
._cs
_section
_inline
_comments
[entry
] = (
1821 configspec
.inline_comments
[entry
])
1822 if not section
.has_key(entry
):
1824 self
._set
_configspec
_value
(configspec
[entry
], section
[entry
])
1826 def _handle_repeat(self
, section
, configspec
):
1827 """Dynamically assign configspec for repeated section."""
1829 section_keys
= configspec
.sections
1830 scalar_keys
= configspec
.scalars
1831 except AttributeError:
1832 section_keys
= [entry
for entry
in configspec
1833 if isinstance(configspec
[entry
], dict)]
1834 scalar_keys
= [entry
for entry
in configspec
1835 if not isinstance(configspec
[entry
], dict)]
1836 if '__many__' in section_keys
and len(section_keys
) > 1:
1837 # FIXME: can we supply any useful information here ?
1838 raise RepeatSectionError
1841 for entry
in scalar_keys
:
1842 val
= configspec
[entry
]
1843 scalars
[entry
] = val
1844 for entry
in section_keys
:
1845 val
= configspec
[entry
]
1846 if entry
== '__many__':
1847 scalars
[entry
] = val
1849 sections
[entry
] = val
1851 section
.configspec
= scalars
1852 for entry
in sections
:
1853 if not section
.has_key(entry
):
1855 self
._handle
_repeat
(section
[entry
], sections
[entry
])
1857 def _write_line(self
, indent_string
, entry
, this_entry
, comment
):
1858 """Write an individual line, for the write method"""
1859 # NOTE: the calls to self._quote here handles non-StringType values.
1861 val
= self
._decode
_element
(self
._quote
(this_entry
))
1863 val
= repr(this_entry
)
1864 return '%s%s%s%s%s' % (
1866 self
._decode
_element
(self
._quote
(entry
, multiline
=False)),
1867 self
._a
_to
_u(' = '),
1869 self
._decode
_element
(comment
))
1871 def _write_marker(self
, indent_string
, depth
, entry
, comment
):
1872 """Write a section marker line"""
1873 return '%s%s%s%s%s' % (
1875 self
._a
_to
_u('[' * depth
),
1876 self
._quote
(self
._decode
_element
(entry
), multiline
=False),
1877 self
._a
_to
_u(']' * depth
),
1878 self
._decode
_element
(comment
))
1880 def _handle_comment(self
, comment
):
1881 """Deal with a comment."""
1884 start
= self
.indent_type
1885 if not comment
.startswith('#'):
1886 start
+= self
._a
_to
_u(' # ')
1887 return (start
+ comment
)
1891 def write(self
, outfile
=None, section
=None):
1893 Write the current ConfigObj as a file
1895 tekNico: FIXME: use StringIO instead of real files
1897 >>> filename = a.filename
1898 >>> a.filename = 'test.ini'
1900 >>> a.filename = filename
1901 >>> a == ConfigObj('test.ini', raise_errors=True)
1904 if self
.indent_type
is None:
1905 # this can be true if initialised from a dictionary
1906 self
.indent_type
= DEFAULT_INDENT_TYPE
1909 cs
= self
._a
_to
_u('#')
1910 csp
= self
._a
_to
_u('# ')
1912 int_val
= self
.interpolation
1913 self
.interpolation
= False
1915 for line
in self
.initial_comment
:
1916 line
= self
._decode
_element
(line
)
1917 stripped_line
= line
.strip()
1918 if stripped_line
and not stripped_line
.startswith(cs
):
1922 indent_string
= self
.indent_type
* section
.depth
1923 for entry
in (section
.scalars
+ section
.sections
):
1924 if entry
in section
.defaults
:
1925 # don't write out default values
1927 for comment_line
in section
.comments
[entry
]:
1928 comment_line
= self
._decode
_element
(comment_line
.lstrip())
1929 if comment_line
and not comment_line
.startswith(cs
):
1930 comment_line
= csp
+ comment_line
1931 out
.append(indent_string
+ comment_line
)
1932 this_entry
= section
[entry
]
1933 comment
= self
._handle
_comment
(section
.inline_comments
[entry
])
1935 if isinstance(this_entry
, dict):
1937 out
.append(self
._write
_marker
(
1942 out
.extend(self
.write(section
=this_entry
))
1944 out
.append(self
._write
_line
(
1951 for line
in self
.final_comment
:
1952 line
= self
._decode
_element
(line
)
1953 stripped_line
= line
.strip()
1954 if stripped_line
and not stripped_line
.startswith(cs
):
1957 self
.interpolation
= int_val
1959 if section
is not self
:
1962 if (self
.filename
is None) and (outfile
is None):
1963 # output a list of lines
1964 # might need to encode
1965 # NOTE: This will *screw* UTF16, each line will start with the BOM
1967 out
= [l
.encode(self
.encoding
) for l
in out
]
1968 if (self
.BOM
and ((self
.encoding
is None) or
1969 (BOM_LIST
.get(self
.encoding
.lower()) == 'utf_8'))):
1973 out
[0] = BOM_UTF8
+ out
[0]
1976 # Turn the list to a string, joined with correct newlines
1977 output
= (self
._a
_to
_u(self
.newlines
or os
.linesep
)
1980 output
= output
.encode(self
.encoding
)
1981 if (self
.BOM
and ((self
.encoding
is None) or
1982 (BOM_LIST
.get(self
.encoding
.lower()) == 'utf_8'))):
1984 output
= BOM_UTF8
+ output
1985 if outfile
is not None:
1986 outfile
.write(output
)
1988 h
= open(self
.filename
, 'wb')
1992 def validate(self
, validator
, preserve_errors
=False, copy
=False,
1995 Test the ConfigObj against a configspec.
1997 It uses the ``validator`` object from *validate.py*.
1999 To run ``validate`` on the current ConfigObj, call: ::
2001 test = config.validate(validator)
2003 (Normally having previously passed in the configspec when the ConfigObj
2004 was created - you can dynamically assign a dictionary of checks to the
2005 ``configspec`` attribute of a section though).
2007 It returns ``True`` if everything passes, or a dictionary of
2008 pass/fails (True/False). If every member of a subsection passes, it
2009 will just have the value ``True``. (It also returns ``False`` if all
2012 In addition, it converts the values from strings to their native
2013 types if their checks pass (and ``stringify`` is set).
2015 If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2016 of a marking a fail with a ``False``, it will preserve the actual
2017 exception object. This can contain info about the reason for failure.
2018 For example the ``VdtValueTooSmallError`` indeicates that the value
2019 supplied was too small. If a value (or section) is missing it will
2020 still be marked as ``False``.
2022 You must have the validate module to use ``preserve_errors=True``.
2024 You can then use the ``flatten_errors`` function to turn your nested
2025 results dictionary into a flattened list of failures - useful for
2026 displaying meaningful error messages.
2029 if self
.configspec
is None:
2030 raise ValueError, 'No configspec supplied.'
2032 if VdtMissingValue
is None:
2033 raise ImportError('Missing validate module.')
2036 spec_section
= section
.configspec
2037 if copy
and hasattr(section
, '_configspec_initial_comment'):
2038 section
.initial_comment
= section
._configspec
_initial
_comment
2039 section
.final_comment
= section
._configspec
_final
_comment
2040 section
.encoding
= section
._configspec
_encoding
2041 section
.BOM
= section
._configspec
_BOM
2042 section
.newlines
= section
._configspec
_newlines
2043 section
.indent_type
= section
._configspec
_indent
_type
2044 if '__many__' in section
.configspec
:
2045 many
= spec_section
['__many__']
2046 # dynamically assign the configspecs
2047 # for the sections below
2048 for entry
in section
.sections
:
2049 self
._handle
_repeat
(section
[entry
], many
)
2054 order
= [k
for k
in section
._order
if k
in spec_section
]
2055 order
+= [k
for k
in spec_section
if k
not in order
]
2057 if entry
== '__many__':
2059 if (not entry
in section
.scalars
) or (entry
in section
.defaults
):
2061 # or entries from defaults
2064 if copy
and not entry
in section
.scalars
:
2066 section
.comments
[entry
] = (
2067 section
._configspec
_comments
.get(entry
, []))
2068 section
.inline_comments
[entry
] = (
2069 section
._configspec
_inline
_comments
.get(entry
, ''))
2073 val
= section
[entry
]
2075 check
= validator
.check(spec_section
[entry
],
2079 except validator
.baseErrorClass
, e
:
2080 if not preserve_errors
or isinstance(e
, VdtMissingValue
):
2083 # preserve the error
2090 if self
.stringify
or missing
:
2091 # if we are doing type conversion
2092 # or the value is a supplied default
2093 if not self
.stringify
:
2094 if isinstance(check
, (list, tuple)):
2096 check
= [self
._str
(item
) for item
in check
]
2097 elif missing
and check
is None:
2098 # convert the None from a default to a ''
2101 check
= self
._str
(check
)
2102 if (check
!= val
) or missing
:
2103 section
[entry
] = check
2104 if not copy
and missing
and entry
not in section
.defaults
:
2105 section
.defaults
.append(entry
)
2107 # Missing sections will have been created as empty ones when the
2108 # configspec was read.
2109 for entry
in section
.sections
:
2110 # FIXME: this means DEFAULT is not copied in copy mode
2111 if section
is self
and entry
== 'DEFAULT':
2114 section
.comments
[entry
] = section
._cs
_section
_comments
[entry
]
2115 section
.inline_comments
[entry
] = (
2116 section
._cs
_section
_inline
_comments
[entry
])
2117 check
= self
.validate(validator
, preserve_errors
=preserve_errors
,
2118 copy
=copy
, section
=section
[entry
])
2135 class SimpleVal(object):
2138 Can be used to check that all members expected are present.
2140 To use it, provide a configspec with all your members in (the value given
2141 will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2142 method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2143 members are present, or a dictionary with True/False meaning
2144 present/missing. (Whole missing sections will be replaced with ``False``)
2148 self
.baseErrorClass
= ConfigObjError
2150 def check(self
, check
, member
, missing
=False):
2151 """A dummy check method, always returns the value unchanged."""
2153 raise self
.baseErrorClass
2156 # Check / processing functions for options
2157 def flatten_errors(cfg
, res
, levels
=None, results
=None):
2159 An example function that will turn a nested dictionary of results
2160 (as returned by ``ConfigObj.validate``) into a flat list.
2162 ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2163 dictionary returned by ``validate``.
2165 (This is a recursive function, so you shouldn't use the ``levels`` or
2166 ``results`` arguments - they are used by the function.
2168 Returns a list of keys that failed. Each member of the list is a tuple :
2171 ([list of sections...], key, result)
2173 If ``validate`` was called with ``preserve_errors=False`` (the default)
2174 then ``result`` will always be ``False``.
2176 *list of sections* is a flattened list of sections that the key was found
2179 If the section was missing then key will be ``None``.
2181 If the value (or section) was missing then ``result`` will be ``False``.
2183 If ``validate`` was called with ``preserve_errors=True`` and a value
2184 was present, but failed the check, then ``result`` will be the exception
2185 object returned. You can use this as a string that describes the failure.
2187 For example *The value "3" is of the wrong type*.
2190 >>> vtor = validate.Validator()
2196 ... another_option = Probably
2198 ... another_option = True
2205 ... option1 = boolean()
2206 ... option2 = boolean()
2207 ... option3 = boolean(default=Bad_value)
2209 ... option1 = boolean()
2210 ... option2 = boolean()
2211 ... option3 = boolean(default=Bad_value)
2213 ... another_option = boolean()
2215 ... another_option = boolean()
2218 ... value2 = integer
2219 ... value3 = integer(0, 10)
2220 ... [[[section3b-sub]]]
2223 ... another_option = boolean()
2225 >>> cs = my_cfg.split('\\n')
2226 >>> ini = my_ini.split('\\n')
2227 >>> cfg = ConfigObj(ini, configspec=cs)
2228 >>> res = cfg.validate(vtor, preserve_errors=True)
2230 >>> for entry in flatten_errors(cfg, res):
2231 ... section_list, key, error = entry
2232 ... section_list.insert(0, '[root]')
2233 ... if key is not None:
2234 ... section_list.append(key)
2236 ... section_list.append('[missing]')
2237 ... section_string = ', '.join(section_list)
2238 ... errors.append((section_string, ' = ', error))
2240 >>> for entry in errors:
2241 ... print entry[0], entry[1], (entry[2] or 0)
2243 [root], option3 = the value "Bad_value" is of the wrong type.
2244 [root], section1, option2 = 0
2245 [root], section1, option3 = the value "Bad_value" is of the wrong type.
2246 [root], section2, another_option = the value "Probably" is of the wrong type.
2247 [root], section3, section3b, section3b-sub, [missing] = 0
2248 [root], section3, section3b, value2 = the value "a" is of the wrong type.
2249 [root], section3, section3b, value3 = the value "11" is too big.
2250 [root], section4, [missing] = 0
2259 results
.append((levels
[:], None, False))
2263 for (key
, val
) in res
.items():
2266 if isinstance(cfg
.get(key
), dict):
2269 flatten_errors(cfg
[key
], val
, levels
, results
)
2271 results
.append((levels
[:], key
, val
))
2279 """*A programming language is a medium of expression.* - Paul Graham"""