Replace our gconf-based settings module with a file-based one
[mevemon] / package / src / configobj.py
1 # configobj.py
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
6
7 # ConfigObj 4
8 # http://www.voidspace.org.uk/python/configobj.html
9
10 # Released subject to the BSD License
11 # Please see http://www.voidspace.org.uk/python/license.shtml
12
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.
18
19 from __future__ import generators
20
21 import os
22 import re
23 import sys
24
25 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
26
27
28 # imported lazily to avoid startup performance hit if it isn't used
29 compiler = None
30
31 # A dictionary mapping BOM to
32 # the encoding to decode with, and what to set the
33 # encoding attribute to.
34 BOMS = {
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'),
39     }
40 # All legal variants of the BOM codecs.
41 # TODO: the list of aliases is not meant to be exhaustive, is there a
42 #   better way ?
43 BOM_LIST = {
44     'utf_16': 'utf_16',
45     'u16': 'utf_16',
46     'utf16': 'utf_16',
47     'utf-16': 'utf_16',
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',
54     'utf_8': 'utf_8',
55     'u8': 'utf_8',
56     'utf': 'utf_8',
57     'utf8': 'utf_8',
58     'utf-8': 'utf_8',
59     }
60
61 # Map of encodings to the BOM to write.
62 BOM_SET = {
63     'utf_8': BOM_UTF8,
64     'utf_16': BOM_UTF16,
65     'utf16_be': BOM_UTF16_BE,
66     'utf16_le': BOM_UTF16_LE,
67     None: BOM_UTF8
68     }
69
70
71 def match_utf8(encoding):
72     return BOM_LIST.get(encoding.lower()) == 'utf_8'
73
74
75 # Quote strings used for writing values
76 squot = "'%s'"
77 dquot = '"%s"'
78 noquot = "%s"
79 wspace_plus = ' \r\n\v\t\'"'
80 tsquot = '"""%s"""'
81 tdquot = "'''%s'''"
82
83 # Sentinel for use in getattr calls to replace hasattr
84 MISSING = object()
85
86 __version__ = '4.7.1'
87
88 try:
89     any
90 except NameError:
91     def any(iterable):
92         for entry in iterable:
93             if entry:
94                 return True
95         return False
96
97
98 __all__ = (
99     '__version__',
100     'DEFAULT_INDENT_TYPE',
101     'DEFAULT_INTERPOLATION',
102     'ConfigObjError',
103     'NestingError',
104     'ParseError',
105     'DuplicateError',
106     'ConfigspecError',
107     'ConfigObj',
108     'SimpleVal',
109     'InterpolationError',
110     'InterpolationLoopError',
111     'MissingInterpolationOption',
112     'RepeatSectionError',
113     'ReloadError',
114     'UnreprError',
115     'UnknownType',
116     'flatten_errors',
117     'get_extra_values'
118 )
119
120 DEFAULT_INTERPOLATION = 'configparser'
121 DEFAULT_INDENT_TYPE = '    '
122 MAX_INTERPOL_DEPTH = 10
123
124 OPTION_DEFAULTS = {
125     'interpolation': True,
126     'raise_errors': False,
127     'list_values': True,
128     'create_empty': False,
129     'file_error': False,
130     'configspec': None,
131     'stringify': True,
132     # option may be set to one of ('', ' ', '\t')
133     'indent_type': None,
134     'encoding': None,
135     'default_encoding': None,
136     'unrepr': False,
137     'write_empty_values': False,
138 }
139
140
141
142 def getObj(s):
143     global compiler
144     if compiler is None:
145         import compiler
146     s = "a=" + s
147     p = compiler.parse(s)
148     return p.getChildren()[1].getChildren()[0].getChildren()[1]
149
150
151 class UnknownType(Exception):
152     pass
153
154
155 class Builder(object):
156     
157     def build(self, o):
158         m = getattr(self, 'build_' + o.__class__.__name__, None)
159         if m is None:
160             raise UnknownType(o.__class__.__name__)
161         return m(o)
162     
163     def build_List(self, o):
164         return map(self.build, o.getChildren())
165     
166     def build_Const(self, o):
167         return o.value
168     
169     def build_Dict(self, o):
170         d = {}
171         i = iter(map(self.build, o.getChildren()))
172         for el in i:
173             d[el] = i.next()
174         return d
175     
176     def build_Tuple(self, o):
177         return tuple(self.build_List(o))
178     
179     def build_Name(self, o):
180         if o.name == 'None':
181             return None
182         if o.name == 'True':
183             return True
184         if o.name == 'False':
185             return False
186         
187         # An undefined Name
188         raise UnknownType('Undefined Name')
189     
190     def build_Add(self, o):
191         real, imag = map(self.build_Const, o.getChildren())
192         try:
193             real = float(real)
194         except TypeError:
195             raise UnknownType('Add')
196         if not isinstance(imag, complex) or imag.real != 0.0:
197             raise UnknownType('Add')
198         return real+imag
199     
200     def build_Getattr(self, o):
201         parent = self.build(o.expr)
202         return getattr(parent, o.attrname)
203     
204     def build_UnarySub(self, o):
205         return -self.build_Const(o.getChildren()[0])
206     
207     def build_UnaryAdd(self, o):
208         return self.build_Const(o.getChildren()[0])
209
210
211 _builder = Builder()
212
213
214 def unrepr(s):
215     if not s:
216         return s
217     return _builder.build(getObj(s))
218
219
220
221 class ConfigObjError(SyntaxError):
222     """
223     This is the base class for all errors that ConfigObj raises.
224     It is a subclass of SyntaxError.
225     """
226     def __init__(self, message='', line_number=None, line=''):
227         self.line = line
228         self.line_number = line_number
229         SyntaxError.__init__(self, message)
230
231
232 class NestingError(ConfigObjError):
233     """
234     This error indicates a level of nesting that doesn't match.
235     """
236
237
238 class ParseError(ConfigObjError):
239     """
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.
243     """
244
245
246 class ReloadError(IOError):
247     """
248     A 'reload' operation failed.
249     This exception is a subclass of ``IOError``.
250     """
251     def __init__(self):
252         IOError.__init__(self, 'reload failed, filename is not set.')
253
254
255 class DuplicateError(ConfigObjError):
256     """
257     The keyword or section specified already exists.
258     """
259
260
261 class ConfigspecError(ConfigObjError):
262     """
263     An error occured whilst parsing a configspec.
264     """
265
266
267 class InterpolationError(ConfigObjError):
268     """Base class for the two interpolation errors."""
269
270
271 class InterpolationLoopError(InterpolationError):
272     """Maximum interpolation depth exceeded in string interpolation."""
273
274     def __init__(self, option):
275         InterpolationError.__init__(
276             self,
277             'interpolation loop detected in value "%s".' % option)
278
279
280 class RepeatSectionError(ConfigObjError):
281     """
282     This error indicates additional sections in a section with a
283     ``__many__`` (repeated) section.
284     """
285
286
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)
292
293
294 class UnreprError(ConfigObjError):
295     """An error parsing in unrepr mode."""
296
297
298
299 class InterpolationEngine(object):
300     """
301     A helper class to help perform string interpolation.
302
303     This class is an abstract base class; its descendants perform
304     the actual work.
305     """
306
307     # compiled regexp to use in self.interpolate()
308     _KEYCRE = re.compile(r"%\(([^)]*)\)s")
309     _cookie = '%'
310
311     def __init__(self, section):
312         # the Section instance that "owns" this engine
313         self.section = section
314
315
316     def interpolate(self, key, value):
317         # short-cut
318         if not self._cookie in value:
319             return value
320         
321         def recursive_interpolate(key, value, section, backtrail):
322             """The function that does the actual work.
323
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
328
329             This is similar to a depth-first-search algorithm.
330             """
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
337
338             # Now start the actual work
339             match = self._KEYCRE.search(value)
340             while match:
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)
344                 if k is None:
345                     # That's the signal that no further interpolation is needed
346                     replacement = v
347                 else:
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)
357
358             # Now safe to come back here again; remove marker from backtrail
359             del backtrail[(key, section.name)]
360
361             return value
362
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, {})
366         return value
367
368
369     def _fetch(self, key):
370         """Helper function to fetch values from owning section.
371
372         Returns a 2-tuple: the value, and the section where it was found.
373         """
374         # switch off interpolation before we try and fetch anything !
375         save_interp = self.section.main.interpolation
376         self.section.main.interpolation = False
377
378         # Start at section that "owns" this InterpolationEngine
379         current_section = self.section
380         while True:
381             # try the current section first
382             val = current_section.get(key)
383             if val is not None:
384                 break
385             # try "DEFAULT" next
386             val = current_section.get('DEFAULT', {}).get(key)
387             if val is not None:
388                 break
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
393                 break
394             current_section = current_section.parent
395
396         # restore interpolation to previous value before returning
397         self.section.main.interpolation = save_interp
398         if val is None:
399             raise MissingInterpolationOption(key)
400         return val, current_section
401
402
403     def _parse_match(self, match):
404         """Implementation-dependent helper function.
405
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)
410
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
414
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 "$").
418         """
419         raise NotImplementedError()
420     
421
422
423 class ConfigParserInterpolation(InterpolationEngine):
424     """Behaves like ConfigParser."""
425     _cookie = '%'
426     _KEYCRE = re.compile(r"%\(([^)]*)\)s")
427
428     def _parse_match(self, match):
429         key = match.group(1)
430         value, section = self._fetch(key)
431         return key, value, section
432
433
434
435 class TemplateInterpolation(InterpolationEngine):
436     """Behaves like string.Template."""
437     _cookie = '$'
438     _delimiter = '$'
439     _KEYCRE = re.compile(r"""
440         \$(?:
441           (?P<escaped>\$)              |   # Two $ signs
442           (?P<named>[_a-z][_a-z0-9]*)  |   # $name format
443           {(?P<braced>[^}]*)}              # ${name} format
444         )
445         """, re.IGNORECASE | re.VERBOSE)
446
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')
450         if key is not None:
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
459
460
461 interpolation_engines = {
462     'configparser': ConfigParserInterpolation,
463     'template': TemplateInterpolation,
464 }
465
466
467 def __newobj__(cls, *args):
468     # Hack for pickle
469     return cls.__new__(cls, *args) 
470
471 class Section(dict):
472     """
473     A dictionary-like object that represents a section in a config file.
474     
475     It does string interpolation if the 'interpolation' attribute
476     of the 'main' object is set to True.
477     
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.
481     
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.
485     
486     Iteration follows the order: scalars, then sections.
487     """
488
489     
490     def __setstate__(self, state):
491         dict.update(self, state[0])
492         self.__dict__.update(state[1])
493
494     def __reduce__(self):
495         state = (dict(self), self.__dict__)
496         return (__newobj__, (self.__class__,), state)
497     
498     
499     def __init__(self, parent, depth, main, indict=None, name=None):
500         """
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
505         """
506         if indict is None:
507             indict = {}
508         dict.__init__(self)
509         # used for nesting level *and* interpolation
510         self.parent = parent
511         # used for the interpolation attribute
512         self.main = main
513         # level of nesting depth of this Section
514         self.depth = depth
515         # purely for information
516         self.name = name
517         #
518         self._initialise()
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():
522             self[entry] = value
523             
524             
525     def _initialise(self):
526         # the sequence of scalar values in this Section
527         self.scalars = []
528         # the sequence of sections in this Section
529         self.sections = []
530         # for comments :-)
531         self.comments = {}
532         self.inline_comments = {}
533         # the configspec
534         self.configspec = None
535         # for defaults
536         self.defaults = []
537         self.default_values = {}
538         self.extra_values = []
539         self._created = False
540
541
542     def _interpolate(self, key, value):
543         try:
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)
554             if class_ is None:
555                 # invalid value for self.main.interpolation
556                 self.main.interpolation = False
557                 return value
558             else:
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)
563
564
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):
572                 def _check(entry):
573                     if isinstance(entry, basestring):
574                         return self._interpolate(key, entry)
575                     return entry
576                 return [_check(entry) for entry in val]
577         return val
578
579
580     def __setitem__(self, key, value, unrepr=False):
581         """
582         Correctly set a value.
583         
584         Making dictionary values Section instances.
585         (We have to special case 'Section' instances - which are also dicts)
586         
587         Keys must be strings.
588         Values need only be strings (or lists of strings) if
589         ``main.stringify`` is set.
590         
591         ``unrepr`` must be set when setting a value to a dictionary, without
592         creating a new sub-section.
593         """
594         if not isinstance(key, basestring):
595             raise ValueError('The key "%s" is not a string.' % key)
596         
597         # add the comment
598         if key not in self.comments:
599             self.comments[key] = []
600             self.inline_comments[key] = ''
601         # remove the entry from defaults
602         if key in self.defaults:
603             self.defaults.remove(key)
604         #
605         if isinstance(value, Section):
606             if key not in self:
607                 self.sections.append(key)
608             dict.__setitem__(self, key, value)
609         elif isinstance(value, dict) and not unrepr:
610             # First create the new depth level,
611             # then create the section
612             if key not in self:
613                 self.sections.append(key)
614             new_depth = self.depth + 1
615             dict.__setitem__(
616                 self,
617                 key,
618                 Section(
619                     self,
620                     new_depth,
621                     self.main,
622                     indict=value,
623                     name=key))
624         else:
625             if key not in self:
626                 self.scalars.append(key)
627             if not self.main.stringify:
628                 if isinstance(value, basestring):
629                     pass
630                 elif isinstance(value, (list, tuple)):
631                     for entry in value:
632                         if not isinstance(entry, basestring):
633                             raise TypeError('Value is not a string "%s".' % entry)
634                 else:
635                     raise TypeError('Value is not a string "%s".' % value)
636             dict.__setitem__(self, key, value)
637
638
639     def __delitem__(self, key):
640         """Remove items from the sequence when deleting."""
641         dict. __delitem__(self, key)
642         if key in self.scalars:
643             self.scalars.remove(key)
644         else:
645             self.sections.remove(key)
646         del self.comments[key]
647         del self.inline_comments[key]
648
649
650     def get(self, key, default=None):
651         """A version of ``get`` that doesn't bypass string interpolation."""
652         try:
653             return self[key]
654         except KeyError:
655             return default
656
657
658     def update(self, indict):
659         """
660         A version of update that uses our ``__setitem__``.
661         """
662         for entry in indict:
663             self[entry] = indict[entry]
664
665
666     def pop(self, key, *args):
667         """
668         'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
669         If key is not found, d is returned if given, otherwise KeyError is raised'
670         """
671         val = dict.pop(self, key, *args)
672         if key in self.scalars:
673             del self.comments[key]
674             del self.inline_comments[key]
675             self.scalars.remove(key)
676         elif key in self.sections:
677             del self.comments[key]
678             del self.inline_comments[key]
679             self.sections.remove(key)
680         if self.main.interpolation and isinstance(val, basestring):
681             return self._interpolate(key, val)
682         return val
683
684
685     def popitem(self):
686         """Pops the first (key,val)"""
687         sequence = (self.scalars + self.sections)
688         if not sequence:
689             raise KeyError(": 'popitem(): dictionary is empty'")
690         key = sequence[0]
691         val =  self[key]
692         del self[key]
693         return key, val
694
695
696     def clear(self):
697         """
698         A version of clear that also affects scalars/sections
699         Also clears comments and configspec.
700         
701         Leaves other attributes alone :
702             depth/main/parent are not affected
703         """
704         dict.clear(self)
705         self.scalars = []
706         self.sections = []
707         self.comments = {}
708         self.inline_comments = {}
709         self.configspec = None
710         self.defaults = []
711         self.extra_values = []
712
713
714     def setdefault(self, key, default=None):
715         """A version of setdefault that sets sequence if appropriate."""
716         try:
717             return self[key]
718         except KeyError:
719             self[key] = default
720             return self[key]
721
722
723     def items(self):
724         """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
725         return zip((self.scalars + self.sections), self.values())
726
727
728     def keys(self):
729         """D.keys() -> list of D's keys"""
730         return (self.scalars + self.sections)
731
732
733     def values(self):
734         """D.values() -> list of D's values"""
735         return [self[key] for key in (self.scalars + self.sections)]
736
737
738     def iteritems(self):
739         """D.iteritems() -> an iterator over the (key, value) items of D"""
740         return iter(self.items())
741
742
743     def iterkeys(self):
744         """D.iterkeys() -> an iterator over the keys of D"""
745         return iter((self.scalars + self.sections))
746
747     __iter__ = iterkeys
748
749
750     def itervalues(self):
751         """D.itervalues() -> an iterator over the values of D"""
752         return iter(self.values())
753
754
755     def __repr__(self):
756         """x.__repr__() <==> repr(x)"""
757         return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
758             for key in (self.scalars + self.sections)])
759
760     __str__ = __repr__
761     __str__.__doc__ = "x.__str__() <==> str(x)"
762
763
764     # Extra methods - not in a normal dictionary
765
766     def dict(self):
767         """
768         Return a deepcopy of self as a dictionary.
769         
770         All members that are ``Section`` instances are recursively turned to
771         ordinary dictionaries - by calling their ``dict`` method.
772         
773         >>> n = a.dict()
774         >>> n == a
775         1
776         >>> n is a
777         0
778         """
779         newdict = {}
780         for entry in self:
781             this_entry = self[entry]
782             if isinstance(this_entry, Section):
783                 this_entry = this_entry.dict()
784             elif isinstance(this_entry, list):
785                 # create a copy rather than a reference
786                 this_entry = list(this_entry)
787             elif isinstance(this_entry, tuple):
788                 # create a copy rather than a reference
789                 this_entry = tuple(this_entry)
790             newdict[entry] = this_entry
791         return newdict
792
793
794     def merge(self, indict):
795         """
796         A recursive update - useful for merging config files.
797         
798         >>> a = '''[section1]
799         ...     option1 = True
800         ...     [[subsection]]
801         ...     more_options = False
802         ...     # end of file'''.splitlines()
803         >>> b = '''# File is user.ini
804         ...     [section1]
805         ...     option1 = False
806         ...     # end of file'''.splitlines()
807         >>> c1 = ConfigObj(b)
808         >>> c2 = ConfigObj(a)
809         >>> c2.merge(c1)
810         >>> c2
811         ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
812         """
813         for key, val in indict.items():
814             if (key in self and isinstance(self[key], dict) and
815                                 isinstance(val, dict)):
816                 self[key].merge(val)
817             else:   
818                 self[key] = val
819
820
821     def rename(self, oldkey, newkey):
822         """
823         Change a keyname to another, without changing position in sequence.
824         
825         Implemented so that transformations can be made on keys,
826         as well as on values. (used by encode and decode)
827         
828         Also renames comments.
829         """
830         if oldkey in self.scalars:
831             the_list = self.scalars
832         elif oldkey in self.sections:
833             the_list = self.sections
834         else:
835             raise KeyError('Key "%s" not found.' % oldkey)
836         pos = the_list.index(oldkey)
837         #
838         val = self[oldkey]
839         dict.__delitem__(self, oldkey)
840         dict.__setitem__(self, newkey, val)
841         the_list.remove(oldkey)
842         the_list.insert(pos, newkey)
843         comm = self.comments[oldkey]
844         inline_comment = self.inline_comments[oldkey]
845         del self.comments[oldkey]
846         del self.inline_comments[oldkey]
847         self.comments[newkey] = comm
848         self.inline_comments[newkey] = inline_comment
849
850
851     def walk(self, function, raise_errors=True,
852             call_on_sections=False, **keywargs):
853         """
854         Walk every member and call a function on the keyword and value.
855         
856         Return a dictionary of the return values
857         
858         If the function raises an exception, raise the errror
859         unless ``raise_errors=False``, in which case set the return value to
860         ``False``.
861         
862         Any unrecognised keyword arguments you pass to walk, will be pased on
863         to the function you pass in.
864         
865         Note: if ``call_on_sections`` is ``True`` then - on encountering a
866         subsection, *first* the function is called for the *whole* subsection,
867         and then recurses into it's members. This means your function must be
868         able to handle strings, dictionaries and lists. This allows you
869         to change the key of subsections as well as for ordinary members. The
870         return value when called on the whole subsection has to be discarded.
871         
872         See  the encode and decode methods for examples, including functions.
873         
874         .. admonition:: caution
875         
876             You can use ``walk`` to transform the names of members of a section
877             but you mustn't add or delete members.
878         
879         >>> config = '''[XXXXsection]
880         ... XXXXkey = XXXXvalue'''.splitlines()
881         >>> cfg = ConfigObj(config)
882         >>> cfg
883         ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
884         >>> def transform(section, key):
885         ...     val = section[key]
886         ...     newkey = key.replace('XXXX', 'CLIENT1')
887         ...     section.rename(key, newkey)
888         ...     if isinstance(val, (tuple, list, dict)):
889         ...         pass
890         ...     else:
891         ...         val = val.replace('XXXX', 'CLIENT1')
892         ...         section[newkey] = val
893         >>> cfg.walk(transform, call_on_sections=True)
894         {'CLIENT1section': {'CLIENT1key': None}}
895         >>> cfg
896         ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
897         """
898         out = {}
899         # scalars first
900         for i in range(len(self.scalars)):
901             entry = self.scalars[i]
902             try:
903                 val = function(self, entry, **keywargs)
904                 # bound again in case name has changed
905                 entry = self.scalars[i]
906                 out[entry] = val
907             except Exception:
908                 if raise_errors:
909                     raise
910                 else:
911                     entry = self.scalars[i]
912                     out[entry] = False
913         # then sections
914         for i in range(len(self.sections)):
915             entry = self.sections[i]
916             if call_on_sections:
917                 try:
918                     function(self, entry, **keywargs)
919                 except Exception:
920                     if raise_errors:
921                         raise
922                     else:
923                         entry = self.sections[i]
924                         out[entry] = False
925                 # bound again in case name has changed
926                 entry = self.sections[i]
927             # previous result is discarded
928             out[entry] = self[entry].walk(
929                 function,
930                 raise_errors=raise_errors,
931                 call_on_sections=call_on_sections,
932                 **keywargs)
933         return out
934
935
936     def as_bool(self, key):
937         """
938         Accepts a key as input. The corresponding value must be a string or
939         the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
940         retain compatibility with Python 2.2.
941         
942         If the string is one of  ``True``, ``On``, ``Yes``, or ``1`` it returns 
943         ``True``.
944         
945         If the string is one of  ``False``, ``Off``, ``No``, or ``0`` it returns 
946         ``False``.
947         
948         ``as_bool`` is not case sensitive.
949         
950         Any other input will raise a ``ValueError``.
951         
952         >>> a = ConfigObj()
953         >>> a['a'] = 'fish'
954         >>> a.as_bool('a')
955         Traceback (most recent call last):
956         ValueError: Value "fish" is neither True nor False
957         >>> a['b'] = 'True'
958         >>> a.as_bool('b')
959         1
960         >>> a['b'] = 'off'
961         >>> a.as_bool('b')
962         0
963         """
964         val = self[key]
965         if val == True:
966             return True
967         elif val == False:
968             return False
969         else:
970             try:
971                 if not isinstance(val, basestring):
972                     # TODO: Why do we raise a KeyError here?
973                     raise KeyError()
974                 else:
975                     return self.main._bools[val.lower()]
976             except KeyError:
977                 raise ValueError('Value "%s" is neither True nor False' % val)
978
979
980     def as_int(self, key):
981         """
982         A convenience method which coerces the specified value to an integer.
983         
984         If the value is an invalid literal for ``int``, a ``ValueError`` will
985         be raised.
986         
987         >>> a = ConfigObj()
988         >>> a['a'] = 'fish'
989         >>> a.as_int('a')
990         Traceback (most recent call last):
991         ValueError: invalid literal for int() with base 10: 'fish'
992         >>> a['b'] = '1'
993         >>> a.as_int('b')
994         1
995         >>> a['b'] = '3.2'
996         >>> a.as_int('b')
997         Traceback (most recent call last):
998         ValueError: invalid literal for int() with base 10: '3.2'
999         """
1000         return int(self[key])
1001
1002
1003     def as_float(self, key):
1004         """
1005         A convenience method which coerces the specified value to a float.
1006         
1007         If the value is an invalid literal for ``float``, a ``ValueError`` will
1008         be raised.
1009         
1010         >>> a = ConfigObj()
1011         >>> a['a'] = 'fish'
1012         >>> a.as_float('a')
1013         Traceback (most recent call last):
1014         ValueError: invalid literal for float(): fish
1015         >>> a['b'] = '1'
1016         >>> a.as_float('b')
1017         1.0
1018         >>> a['b'] = '3.2'
1019         >>> a.as_float('b')
1020         3.2000000000000002
1021         """
1022         return float(self[key])
1023     
1024     
1025     def as_list(self, key):
1026         """
1027         A convenience method which fetches the specified value, guaranteeing
1028         that it is a list.
1029         
1030         >>> a = ConfigObj()
1031         >>> a['a'] = 1
1032         >>> a.as_list('a')
1033         [1]
1034         >>> a['a'] = (1,)
1035         >>> a.as_list('a')
1036         [1]
1037         >>> a['a'] = [1]
1038         >>> a.as_list('a')
1039         [1]
1040         """
1041         result = self[key]
1042         if isinstance(result, (tuple, list)):
1043             return list(result)
1044         return [result]
1045         
1046
1047     def restore_default(self, key):
1048         """
1049         Restore (and return) default value for the specified key.
1050         
1051         This method will only work for a ConfigObj that was created
1052         with a configspec and has been validated.
1053         
1054         If there is no default value for this key, ``KeyError`` is raised.
1055         """
1056         default = self.default_values[key]
1057         dict.__setitem__(self, key, default)
1058         if key not in self.defaults:
1059             self.defaults.append(key)
1060         return default
1061
1062     
1063     def restore_defaults(self):
1064         """
1065         Recursively restore default values to all members
1066         that have them.
1067         
1068         This method will only work for a ConfigObj that was created
1069         with a configspec and has been validated.
1070         
1071         It doesn't delete or modify entries without default values.
1072         """
1073         for key in self.default_values:
1074             self.restore_default(key)
1075             
1076         for section in self.sections:
1077             self[section].restore_defaults()
1078
1079
1080 class ConfigObj(Section):
1081     """An object to read, create, and write config files."""
1082
1083     _keyword = re.compile(r'''^ # line start
1084         (\s*)                   # indentation
1085         (                       # keyword
1086             (?:".*?")|          # double quotes
1087             (?:'.*?')|          # single quotes
1088             (?:[^'"=].*?)       # no quotes
1089         )
1090         \s*=\s*                 # divider
1091         (.*)                    # value (including list values and comments)
1092         $   # line end
1093         ''',
1094         re.VERBOSE)
1095
1096     _sectionmarker = re.compile(r'''^
1097         (\s*)                     # 1: indentation
1098         ((?:\[\s*)+)              # 2: section marker open
1099         (                         # 3: section name open
1100             (?:"\s*\S.*?\s*")|    # at least one non-space with double quotes
1101             (?:'\s*\S.*?\s*')|    # at least one non-space with single quotes
1102             (?:[^'"\s].*?)        # at least one non-space unquoted
1103         )                         # section name close
1104         ((?:\s*\])+)              # 4: section marker close
1105         \s*(\#.*)?                # 5: optional comment
1106         $''',
1107         re.VERBOSE)
1108
1109     # this regexp pulls list values out as a single string
1110     # or single values and comments
1111     # FIXME: this regex adds a '' to the end of comma terminated lists
1112     #   workaround in ``_handle_value``
1113     _valueexp = re.compile(r'''^
1114         (?:
1115             (?:
1116                 (
1117                     (?:
1118                         (?:
1119                             (?:".*?")|              # double quotes
1120                             (?:'.*?')|              # single quotes
1121                             (?:[^'",\#][^,\#]*?)    # unquoted
1122                         )
1123                         \s*,\s*                     # comma
1124                     )*      # match all list items ending in a comma (if any)
1125                 )
1126                 (
1127                     (?:".*?")|                      # double quotes
1128                     (?:'.*?')|                      # single quotes
1129                     (?:[^'",\#\s][^,]*?)|           # unquoted
1130                     (?:(?<!,))                      # Empty value
1131                 )?          # last item in a list - or string value
1132             )|
1133             (,)             # alternatively a single comma - empty list
1134         )
1135         \s*(\#.*)?          # optional comment
1136         $''',
1137         re.VERBOSE)
1138
1139     # use findall to get the members of a list value
1140     _listvalueexp = re.compile(r'''
1141         (
1142             (?:".*?")|          # double quotes
1143             (?:'.*?')|          # single quotes
1144             (?:[^'",\#]?.*?)       # unquoted
1145         )
1146         \s*,\s*                 # comma
1147         ''',
1148         re.VERBOSE)
1149
1150     # this regexp is used for the value
1151     # when lists are switched off
1152     _nolistvalue = re.compile(r'''^
1153         (
1154             (?:".*?")|          # double quotes
1155             (?:'.*?')|          # single quotes
1156             (?:[^'"\#].*?)|     # unquoted
1157             (?:)                # Empty value
1158         )
1159         \s*(\#.*)?              # optional comment
1160         $''',
1161         re.VERBOSE)
1162
1163     # regexes for finding triple quoted values on one line
1164     _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1165     _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1166     _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1167     _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1168
1169     _triple_quote = {
1170         "'''": (_single_line_single, _multi_line_single),
1171         '"""': (_single_line_double, _multi_line_double),
1172     }
1173
1174     # Used by the ``istrue`` Section method
1175     _bools = {
1176         'yes': True, 'no': False,
1177         'on': True, 'off': False,
1178         '1': True, '0': False,
1179         'true': True, 'false': False,
1180         }
1181
1182
1183     def __init__(self, infile=None, options=None, configspec=None, encoding=None,
1184                  interpolation=True, raise_errors=False, list_values=True,
1185                  create_empty=False, file_error=False, stringify=True,
1186                  indent_type=None, default_encoding=None, unrepr=False,
1187                  write_empty_values=False, _inspec=False):
1188         """
1189         Parse a config file or create a config file object.
1190         
1191         ``ConfigObj(infile=None, configspec=None, encoding=None,
1192                     interpolation=True, raise_errors=False, list_values=True,
1193                     create_empty=False, file_error=False, stringify=True,
1194                     indent_type=None, default_encoding=None, unrepr=False,
1195                     write_empty_values=False, _inspec=False)``
1196         """
1197         self._inspec = _inspec
1198         # init the superclass
1199         Section.__init__(self, self, 0, self)
1200         
1201         infile = infile or []
1202         
1203         _options = {'configspec': configspec,
1204                     'encoding': encoding, 'interpolation': interpolation,
1205                     'raise_errors': raise_errors, 'list_values': list_values,
1206                     'create_empty': create_empty, 'file_error': file_error,
1207                     'stringify': stringify, 'indent_type': indent_type,
1208                     'default_encoding': default_encoding, 'unrepr': unrepr,
1209                     'write_empty_values': write_empty_values}
1210
1211         if options is None:
1212             options = _options
1213         else:
1214             import warnings
1215             warnings.warn('Passing in an options dictionary to ConfigObj() is '
1216                           'deprecated. Use **options instead.',
1217                           DeprecationWarning, stacklevel=2)
1218             
1219             # TODO: check the values too.
1220             for entry in options:
1221                 if entry not in OPTION_DEFAULTS:
1222                     raise TypeError('Unrecognised option "%s".' % entry)
1223             for entry, value in OPTION_DEFAULTS.items():
1224                 if entry not in options:
1225                     options[entry] = value
1226                 keyword_value = _options[entry]
1227                 if value != keyword_value:
1228                     options[entry] = keyword_value
1229         
1230         # XXXX this ignores an explicit list_values = True in combination
1231         # with _inspec. The user should *never* do that anyway, but still...
1232         if _inspec:
1233             options['list_values'] = False
1234         
1235         self._initialise(options)
1236         configspec = options['configspec']
1237         self._original_configspec = configspec
1238         self._load(infile, configspec)
1239         
1240         
1241     def _load(self, infile, configspec):
1242         if isinstance(infile, basestring):
1243             self.filename = infile
1244             if os.path.isfile(infile):
1245                 h = open(infile, 'rb')
1246                 infile = h.read() or []
1247                 h.close()
1248             elif self.file_error:
1249                 # raise an error if the file doesn't exist
1250                 raise IOError('Config file not found: "%s".' % self.filename)
1251             else:
1252                 # file doesn't already exist
1253                 if self.create_empty:
1254                     # this is a good test that the filename specified
1255                     # isn't impossible - like on a non-existent device
1256                     h = open(infile, 'w')
1257                     h.write('')
1258                     h.close()
1259                 infile = []
1260                 
1261         elif isinstance(infile, (list, tuple)):
1262             infile = list(infile)
1263             
1264         elif isinstance(infile, dict):
1265             # initialise self
1266             # the Section class handles creating subsections
1267             if isinstance(infile, ConfigObj):
1268                 # get a copy of our ConfigObj
1269                 def set_section(in_section, this_section):
1270                     for entry in in_section.scalars:
1271                         this_section[entry] = in_section[entry]
1272                     for section in in_section.sections:
1273                         this_section[section] = {}
1274                         set_section(in_section[section], this_section[section])
1275                 set_section(infile, self)
1276                 
1277             else:
1278                 for entry in infile:
1279                     self[entry] = infile[entry]
1280             del self._errors
1281             
1282             if configspec is not None:
1283                 self._handle_configspec(configspec)
1284             else:
1285                 self.configspec = None
1286             return
1287         
1288         elif getattr(infile, 'read', MISSING) is not MISSING:
1289             # This supports file like objects
1290             infile = infile.read() or []
1291             # needs splitting into lines - but needs doing *after* decoding
1292             # in case it's not an 8 bit encoding
1293         else:
1294             raise TypeError('infile must be a filename, file like object, or list of lines.')
1295         
1296         if infile:
1297             # don't do it for the empty ConfigObj
1298             infile = self._handle_bom(infile)
1299             # infile is now *always* a list
1300             #
1301             # Set the newlines attribute (first line ending it finds)
1302             # and strip trailing '\n' or '\r' from lines
1303             for line in infile:
1304                 if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1305                     continue
1306                 for end in ('\r\n', '\n', '\r'):
1307                     if line.endswith(end):
1308                         self.newlines = end
1309                         break
1310                 break
1311
1312             infile = [line.rstrip('\r\n') for line in infile]
1313             
1314         self._parse(infile)
1315         # if we had any errors, now is the time to raise them
1316         if self._errors:
1317             info = "at line %s." % self._errors[0].line_number
1318             if len(self._errors) > 1:
1319                 msg = "Parsing failed with several errors.\nFirst error %s" % info
1320                 error = ConfigObjError(msg)
1321             else:
1322                 error = self._errors[0]
1323             # set the errors attribute; it's a list of tuples:
1324             # (error_type, message, line_number)
1325             error.errors = self._errors
1326             # set the config attribute
1327             error.config = self
1328             raise error
1329         # delete private attributes
1330         del self._errors
1331         
1332         if configspec is None:
1333             self.configspec = None
1334         else:
1335             self._handle_configspec(configspec)
1336     
1337     
1338     def _initialise(self, options=None):
1339         if options is None:
1340             options = OPTION_DEFAULTS
1341             
1342         # initialise a few variables
1343         self.filename = None
1344         self._errors = []
1345         self.raise_errors = options['raise_errors']
1346         self.interpolation = options['interpolation']
1347         self.list_values = options['list_values']
1348         self.create_empty = options['create_empty']
1349         self.file_error = options['file_error']
1350         self.stringify = options['stringify']
1351         self.indent_type = options['indent_type']
1352         self.encoding = options['encoding']
1353         self.default_encoding = options['default_encoding']
1354         self.BOM = False
1355         self.newlines = None
1356         self.write_empty_values = options['write_empty_values']
1357         self.unrepr = options['unrepr']
1358         
1359         self.initial_comment = []
1360         self.final_comment = []
1361         self.configspec = None
1362         
1363         if self._inspec:
1364             self.list_values = False
1365         
1366         # Clear section attributes as well
1367         Section._initialise(self)
1368         
1369         
1370     def __repr__(self):
1371         return ('ConfigObj({%s})' % 
1372                 ', '.join([('%s: %s' % (repr(key), repr(self[key]))) 
1373                 for key in (self.scalars + self.sections)]))
1374     
1375     
1376     def _handle_bom(self, infile):
1377         """
1378         Handle any BOM, and decode if necessary.
1379         
1380         If an encoding is specified, that *must* be used - but the BOM should
1381         still be removed (and the BOM attribute set).
1382         
1383         (If the encoding is wrongly specified, then a BOM for an alternative
1384         encoding won't be discovered or removed.)
1385         
1386         If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1387         removed. The BOM attribute will be set. UTF16 will be decoded to
1388         unicode.
1389         
1390         NOTE: This method must not be called with an empty ``infile``.
1391         
1392         Specifying the *wrong* encoding is likely to cause a
1393         ``UnicodeDecodeError``.
1394         
1395         ``infile`` must always be returned as a list of lines, but may be
1396         passed in as a single string.
1397         """
1398         if ((self.encoding is not None) and
1399             (self.encoding.lower() not in BOM_LIST)):
1400             # No need to check for a BOM
1401             # the encoding specified doesn't have one
1402             # just decode
1403             return self._decode(infile, self.encoding)
1404         
1405         if isinstance(infile, (list, tuple)):
1406             line = infile[0]
1407         else:
1408             line = infile
1409         if self.encoding is not None:
1410             # encoding explicitly supplied
1411             # And it could have an associated BOM
1412             # TODO: if encoding is just UTF16 - we ought to check for both
1413             # TODO: big endian and little endian versions.
1414             enc = BOM_LIST[self.encoding.lower()]
1415             if enc == 'utf_16':
1416                 # For UTF16 we try big endian and little endian
1417                 for BOM, (encoding, final_encoding) in BOMS.items():
1418                     if not final_encoding:
1419                         # skip UTF8
1420                         continue
1421                     if infile.startswith(BOM):
1422                         ### BOM discovered
1423                         ##self.BOM = True
1424                         # Don't need to remove BOM
1425                         return self._decode(infile, encoding)
1426                     
1427                 # If we get this far, will *probably* raise a DecodeError
1428                 # As it doesn't appear to start with a BOM
1429                 return self._decode(infile, self.encoding)
1430             
1431             # Must be UTF8
1432             BOM = BOM_SET[enc]
1433             if not line.startswith(BOM):
1434                 return self._decode(infile, self.encoding)
1435             
1436             newline = line[len(BOM):]
1437             
1438             # BOM removed
1439             if isinstance(infile, (list, tuple)):
1440                 infile[0] = newline
1441             else:
1442                 infile = newline
1443             self.BOM = True
1444             return self._decode(infile, self.encoding)
1445         
1446         # No encoding specified - so we need to check for UTF8/UTF16
1447         for BOM, (encoding, final_encoding) in BOMS.items():
1448             if not line.startswith(BOM):
1449                 continue
1450             else:
1451                 # BOM discovered
1452                 self.encoding = final_encoding
1453                 if not final_encoding:
1454                     self.BOM = True
1455                     # UTF8
1456                     # remove BOM
1457                     newline = line[len(BOM):]
1458                     if isinstance(infile, (list, tuple)):
1459                         infile[0] = newline
1460                     else:
1461                         infile = newline
1462                     # UTF8 - don't decode
1463                     if isinstance(infile, basestring):
1464                         return infile.splitlines(True)
1465                     else:
1466                         return infile
1467                 # UTF16 - have to decode
1468                 return self._decode(infile, encoding)
1469             
1470         # No BOM discovered and no encoding specified, just return
1471         if isinstance(infile, basestring):
1472             # infile read from a file will be a single string
1473             return infile.splitlines(True)
1474         return infile
1475
1476
1477     def _a_to_u(self, aString):
1478         """Decode ASCII strings to unicode if a self.encoding is specified."""
1479         if self.encoding:
1480             return aString.decode('ascii')
1481         else:
1482             return aString
1483
1484
1485     def _decode(self, infile, encoding):
1486         """
1487         Decode infile to unicode. Using the specified encoding.
1488         
1489         if is a string, it also needs converting to a list.
1490         """
1491         if isinstance(infile, basestring):
1492             # can't be unicode
1493             # NOTE: Could raise a ``UnicodeDecodeError``
1494             return infile.decode(encoding).splitlines(True)
1495         for i, line in enumerate(infile):
1496             if not isinstance(line, unicode):
1497                 # NOTE: The isinstance test here handles mixed lists of unicode/string
1498                 # NOTE: But the decode will break on any non-string values
1499                 # NOTE: Or could raise a ``UnicodeDecodeError``
1500                 infile[i] = line.decode(encoding)
1501         return infile
1502
1503
1504     def _decode_element(self, line):
1505         """Decode element to unicode if necessary."""
1506         if not self.encoding:
1507             return line
1508         if isinstance(line, str) and self.default_encoding:
1509             return line.decode(self.default_encoding)
1510         return line
1511
1512
1513     def _str(self, value):
1514         """
1515         Used by ``stringify`` within validate, to turn non-string values
1516         into strings.
1517         """
1518         if not isinstance(value, basestring):
1519             return str(value)
1520         else:
1521             return value
1522
1523
1524     def _parse(self, infile):
1525         """Actually parse the config file."""
1526         temp_list_values = self.list_values
1527         if self.unrepr:
1528             self.list_values = False
1529             
1530         comment_list = []
1531         done_start = False
1532         this_section = self
1533         maxline = len(infile) - 1
1534         cur_index = -1
1535         reset_comment = False
1536         
1537         while cur_index < maxline:
1538             if reset_comment:
1539                 comment_list = []
1540             cur_index += 1
1541             line = infile[cur_index]
1542             sline = line.strip()
1543             # do we have anything on the line ?
1544             if not sline or sline.startswith('#'):
1545                 reset_comment = False
1546                 comment_list.append(line)
1547                 continue
1548             
1549             if not done_start:
1550                 # preserve initial comment
1551                 self.initial_comment = comment_list
1552                 comment_list = []
1553                 done_start = True
1554                 
1555             reset_comment = True
1556             # first we check if it's a section marker
1557             mat = self._sectionmarker.match(line)
1558             if mat is not None:
1559                 # is a section line
1560                 (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1561                 if indent and (self.indent_type is None):
1562                     self.indent_type = indent
1563                 cur_depth = sect_open.count('[')
1564                 if cur_depth != sect_close.count(']'):
1565                     self._handle_error("Cannot compute the section depth at line %s.",
1566                                        NestingError, infile, cur_index)
1567                     continue
1568                 
1569                 if cur_depth < this_section.depth:
1570                     # the new section is dropping back to a previous level
1571                     try:
1572                         parent = self._match_depth(this_section,
1573                                                    cur_depth).parent
1574                     except SyntaxError:
1575                         self._handle_error("Cannot compute nesting level at line %s.",
1576                                            NestingError, infile, cur_index)
1577                         continue
1578                 elif cur_depth == this_section.depth:
1579                     # the new section is a sibling of the current section
1580                     parent = this_section.parent
1581                 elif cur_depth == this_section.depth + 1:
1582                     # the new section is a child the current section
1583                     parent = this_section
1584                 else:
1585                     self._handle_error("Section too nested at line %s.",
1586                                        NestingError, infile, cur_index)
1587                     
1588                 sect_name = self._unquote(sect_name)
1589                 if sect_name in parent:
1590                     self._handle_error('Duplicate section name at line %s.',
1591                                        DuplicateError, infile, cur_index)
1592                     continue
1593                 
1594                 # create the new section
1595                 this_section = Section(
1596                     parent,
1597                     cur_depth,
1598                     self,
1599                     name=sect_name)
1600                 parent[sect_name] = this_section
1601                 parent.inline_comments[sect_name] = comment
1602                 parent.comments[sect_name] = comment_list
1603                 continue
1604             #
1605             # it's not a section marker,
1606             # so it should be a valid ``key = value`` line
1607             mat = self._keyword.match(line)
1608             if mat is None:
1609                 # it neither matched as a keyword
1610                 # or a section marker
1611                 self._handle_error(
1612                     'Invalid line at line "%s".',
1613                     ParseError, infile, cur_index)
1614             else:
1615                 # is a keyword value
1616                 # value will include any inline comment
1617                 (indent, key, value) = mat.groups()
1618                 if indent and (self.indent_type is None):
1619                     self.indent_type = indent
1620                 # check for a multiline value
1621                 if value[:3] in ['"""', "'''"]:
1622                     try:
1623                         value, comment, cur_index = self._multiline(
1624                             value, infile, cur_index, maxline)
1625                     except SyntaxError:
1626                         self._handle_error(
1627                             'Parse error in value at line %s.',
1628                             ParseError, infile, cur_index)
1629                         continue
1630                     else:
1631                         if self.unrepr:
1632                             comment = ''
1633                             try:
1634                                 value = unrepr(value)
1635                             except Exception, e:
1636                                 if type(e) == UnknownType:
1637                                     msg = 'Unknown name or type in value at line %s.'
1638                                 else:
1639                                     msg = 'Parse error in value at line %s.'
1640                                 self._handle_error(msg, UnreprError, infile,
1641                                     cur_index)
1642                                 continue
1643                 else:
1644                     if self.unrepr:
1645                         comment = ''
1646                         try:
1647                             value = unrepr(value)
1648                         except Exception, e:
1649                             if isinstance(e, UnknownType):
1650                                 msg = 'Unknown name or type in value at line %s.'
1651                             else:
1652                                 msg = 'Parse error in value at line %s.'
1653                             self._handle_error(msg, UnreprError, infile,
1654                                 cur_index)
1655                             continue
1656                     else:
1657                         # extract comment and lists
1658                         try:
1659                             (value, comment) = self._handle_value(value)
1660                         except SyntaxError:
1661                             self._handle_error(
1662                                 'Parse error in value at line %s.',
1663                                 ParseError, infile, cur_index)
1664                             continue
1665                 #
1666                 key = self._unquote(key)
1667                 if key in this_section:
1668                     self._handle_error(
1669                         'Duplicate keyword name at line %s.',
1670                         DuplicateError, infile, cur_index)
1671                     continue
1672                 # add the key.
1673                 # we set unrepr because if we have got this far we will never
1674                 # be creating a new section
1675                 this_section.__setitem__(key, value, unrepr=True)
1676                 this_section.inline_comments[key] = comment
1677                 this_section.comments[key] = comment_list
1678                 continue
1679         #
1680         if self.indent_type is None:
1681             # no indentation used, set the type accordingly
1682             self.indent_type = ''
1683
1684         # preserve the final comment
1685         if not self and not self.initial_comment:
1686             self.initial_comment = comment_list
1687         elif not reset_comment:
1688             self.final_comment = comment_list
1689         self.list_values = temp_list_values
1690
1691
1692     def _match_depth(self, sect, depth):
1693         """
1694         Given a section and a depth level, walk back through the sections
1695         parents to see if the depth level matches a previous section.
1696         
1697         Return a reference to the right section,
1698         or raise a SyntaxError.
1699         """
1700         while depth < sect.depth:
1701             if sect is sect.parent:
1702                 # we've reached the top level already
1703                 raise SyntaxError()
1704             sect = sect.parent
1705         if sect.depth == depth:
1706             return sect
1707         # shouldn't get here
1708         raise SyntaxError()
1709
1710
1711     def _handle_error(self, text, ErrorClass, infile, cur_index):
1712         """
1713         Handle an error according to the error settings.
1714         
1715         Either raise the error or store it.
1716         The error will have occured at ``cur_index``
1717         """
1718         line = infile[cur_index]
1719         cur_index += 1
1720         message = text % cur_index
1721         error = ErrorClass(message, cur_index, line)
1722         if self.raise_errors:
1723             # raise the error - parsing stops here
1724             raise error
1725         # store the error
1726         # reraise when parsing has finished
1727         self._errors.append(error)
1728
1729
1730     def _unquote(self, value):
1731         """Return an unquoted version of a value"""
1732         if not value:
1733             # should only happen during parsing of lists
1734             raise SyntaxError
1735         if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1736             value = value[1:-1]
1737         return value
1738
1739
1740     def _quote(self, value, multiline=True):
1741         """
1742         Return a safely quoted version of a value.
1743         
1744         Raise a ConfigObjError if the value cannot be safely quoted.
1745         If multiline is ``True`` (default) then use triple quotes
1746         if necessary.
1747         
1748         * Don't quote values that don't need it.
1749         * Recursively quote members of a list and return a comma joined list.
1750         * Multiline is ``False`` for lists.
1751         * Obey list syntax for empty and single member lists.
1752         
1753         If ``list_values=False`` then the value is only quoted if it contains
1754         a ``\\n`` (is multiline) or '#'.
1755         
1756         If ``write_empty_values`` is set, and the value is an empty string, it
1757         won't be quoted.
1758         """
1759         if multiline and self.write_empty_values and value == '':
1760             # Only if multiline is set, so that it is used for values not
1761             # keys, and not values that are part of a list
1762             return ''
1763         
1764         if multiline and isinstance(value, (list, tuple)):
1765             if not value:
1766                 return ','
1767             elif len(value) == 1:
1768                 return self._quote(value[0], multiline=False) + ','
1769             return ', '.join([self._quote(val, multiline=False)
1770                 for val in value])
1771         if not isinstance(value, basestring):
1772             if self.stringify:
1773                 value = str(value)
1774             else:
1775                 raise TypeError('Value "%s" is not a string.' % value)
1776
1777         if not value:
1778             return '""'
1779         
1780         no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1781         need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1782         hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1783         check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1784         
1785         if check_for_single:
1786             if not self.list_values:
1787                 # we don't quote if ``list_values=False``
1788                 quot = noquot
1789             # for normal values either single or double quotes will do
1790             elif '\n' in value:
1791                 # will only happen if multiline is off - e.g. '\n' in key
1792                 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1793             elif ((value[0] not in wspace_plus) and
1794                     (value[-1] not in wspace_plus) and
1795                     (',' not in value)):
1796                 quot = noquot
1797             else:
1798                 quot = self._get_single_quote(value)
1799         else:
1800             # if value has '\n' or "'" *and* '"', it will need triple quotes
1801             quot = self._get_triple_quote(value)
1802         
1803         if quot == noquot and '#' in value and self.list_values:
1804             quot = self._get_single_quote(value)
1805                 
1806         return quot % value
1807     
1808     
1809     def _get_single_quote(self, value):
1810         if ("'" in value) and ('"' in value):
1811             raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1812         elif '"' in value:
1813             quot = squot
1814         else:
1815             quot = dquot
1816         return quot
1817     
1818     
1819     def _get_triple_quote(self, value):
1820         if (value.find('"""') != -1) and (value.find("'''") != -1):
1821             raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1822         if value.find('"""') == -1:
1823             quot = tdquot
1824         else:
1825             quot = tsquot 
1826         return quot
1827
1828
1829     def _handle_value(self, value):
1830         """
1831         Given a value string, unquote, remove comment,
1832         handle lists. (including empty and single member lists)
1833         """
1834         if self._inspec:
1835             # Parsing a configspec so don't handle comments
1836             return (value, '')
1837         # do we look for lists in values ?
1838         if not self.list_values:
1839             mat = self._nolistvalue.match(value)
1840             if mat is None:
1841                 raise SyntaxError()
1842             # NOTE: we don't unquote here
1843             return mat.groups()
1844         #
1845         mat = self._valueexp.match(value)
1846         if mat is None:
1847             # the value is badly constructed, probably badly quoted,
1848             # or an invalid list
1849             raise SyntaxError()
1850         (list_values, single, empty_list, comment) = mat.groups()
1851         if (list_values == '') and (single is None):
1852             # change this if you want to accept empty values
1853             raise SyntaxError()
1854         # NOTE: note there is no error handling from here if the regex
1855         # is wrong: then incorrect values will slip through
1856         if empty_list is not None:
1857             # the single comma - meaning an empty list
1858             return ([], comment)
1859         if single is not None:
1860             # handle empty values
1861             if list_values and not single:
1862                 # FIXME: the '' is a workaround because our regex now matches
1863                 #   '' at the end of a list if it has a trailing comma
1864                 single = None
1865             else:
1866                 single = single or '""'
1867                 single = self._unquote(single)
1868         if list_values == '':
1869             # not a list value
1870             return (single, comment)
1871         the_list = self._listvalueexp.findall(list_values)
1872         the_list = [self._unquote(val) for val in the_list]
1873         if single is not None:
1874             the_list += [single]
1875         return (the_list, comment)
1876
1877
1878     def _multiline(self, value, infile, cur_index, maxline):
1879         """Extract the value, where we are in a multiline situation."""
1880         quot = value[:3]
1881         newvalue = value[3:]
1882         single_line = self._triple_quote[quot][0]
1883         multi_line = self._triple_quote[quot][1]
1884         mat = single_line.match(value)
1885         if mat is not None:
1886             retval = list(mat.groups())
1887             retval.append(cur_index)
1888             return retval
1889         elif newvalue.find(quot) != -1:
1890             # somehow the triple quote is missing
1891             raise SyntaxError()
1892         #
1893         while cur_index < maxline:
1894             cur_index += 1
1895             newvalue += '\n'
1896             line = infile[cur_index]
1897             if line.find(quot) == -1:
1898                 newvalue += line
1899             else:
1900                 # end of multiline, process it
1901                 break
1902         else:
1903             # we've got to the end of the config, oops...
1904             raise SyntaxError()
1905         mat = multi_line.match(line)
1906         if mat is None:
1907             # a badly formed line
1908             raise SyntaxError()
1909         (value, comment) = mat.groups()
1910         return (newvalue + value, comment, cur_index)
1911
1912
1913     def _handle_configspec(self, configspec):
1914         """Parse the configspec."""
1915         # FIXME: Should we check that the configspec was created with the 
1916         #        correct settings ? (i.e. ``list_values=False``)
1917         if not isinstance(configspec, ConfigObj):
1918             try:
1919                 configspec = ConfigObj(configspec,
1920                                        raise_errors=True,
1921                                        file_error=True,
1922                                        _inspec=True)
1923             except ConfigObjError, e:
1924                 # FIXME: Should these errors have a reference
1925                 #        to the already parsed ConfigObj ?
1926                 raise ConfigspecError('Parsing configspec failed: %s' % e)
1927             except IOError, e:
1928                 raise IOError('Reading configspec failed: %s' % e)
1929         
1930         self.configspec = configspec
1931             
1932
1933         
1934     def _set_configspec(self, section, copy):
1935         """
1936         Called by validate. Handles setting the configspec on subsections
1937         including sections to be validated by __many__
1938         """
1939         configspec = section.configspec
1940         many = configspec.get('__many__')
1941         if isinstance(many, dict):
1942             for entry in section.sections:
1943                 if entry not in configspec:
1944                     section[entry].configspec = many
1945                     
1946         for entry in configspec.sections:
1947             if entry == '__many__':
1948                 continue
1949             if entry not in section:
1950                 section[entry] = {}
1951                 section[entry]._created = True
1952                 if copy:
1953                     # copy comments
1954                     section.comments[entry] = configspec.comments.get(entry, [])
1955                     section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1956                 
1957             # Could be a scalar when we expect a section
1958             if isinstance(section[entry], Section):
1959                 section[entry].configspec = configspec[entry]
1960                         
1961
1962     def _write_line(self, indent_string, entry, this_entry, comment):
1963         """Write an individual line, for the write method"""
1964         # NOTE: the calls to self._quote here handles non-StringType values.
1965         if not self.unrepr:
1966             val = self._decode_element(self._quote(this_entry))
1967         else:
1968             val = repr(this_entry)
1969         return '%s%s%s%s%s' % (indent_string,
1970                                self._decode_element(self._quote(entry, multiline=False)),
1971                                self._a_to_u(' = '),
1972                                val,
1973                                self._decode_element(comment))
1974
1975
1976     def _write_marker(self, indent_string, depth, entry, comment):
1977         """Write a section marker line"""
1978         return '%s%s%s%s%s' % (indent_string,
1979                                self._a_to_u('[' * depth),
1980                                self._quote(self._decode_element(entry), multiline=False),
1981                                self._a_to_u(']' * depth),
1982                                self._decode_element(comment))
1983
1984
1985     def _handle_comment(self, comment):
1986         """Deal with a comment."""
1987         if not comment:
1988             return ''
1989         start = self.indent_type
1990         if not comment.startswith('#'):
1991             start += self._a_to_u(' # ')
1992         return (start + comment)
1993
1994
1995     # Public methods
1996
1997     def write(self, outfile=None, section=None):
1998         """
1999         Write the current ConfigObj as a file
2000         
2001         tekNico: FIXME: use StringIO instead of real files
2002         
2003         >>> filename = a.filename
2004         >>> a.filename = 'test.ini'
2005         >>> a.write()
2006         >>> a.filename = filename
2007         >>> a == ConfigObj('test.ini', raise_errors=True)
2008         1
2009         """
2010         if self.indent_type is None:
2011             # this can be true if initialised from a dictionary
2012             self.indent_type = DEFAULT_INDENT_TYPE
2013             
2014         out = []
2015         cs = self._a_to_u('#')
2016         csp = self._a_to_u('# ')
2017         if section is None:
2018             int_val = self.interpolation
2019             self.interpolation = False
2020             section = self
2021             for line in self.initial_comment:
2022                 line = self._decode_element(line)
2023                 stripped_line = line.strip()
2024                 if stripped_line and not stripped_line.startswith(cs):
2025                     line = csp + line
2026                 out.append(line)
2027                 
2028         indent_string = self.indent_type * section.depth
2029         for entry in (section.scalars + section.sections):
2030             if entry in section.defaults:
2031                 # don't write out default values
2032                 continue
2033             for comment_line in section.comments[entry]:
2034                 comment_line = self._decode_element(comment_line.lstrip())
2035                 if comment_line and not comment_line.startswith(cs):
2036                     comment_line = csp + comment_line
2037                 out.append(indent_string + comment_line)
2038             this_entry = section[entry]
2039             comment = self._handle_comment(section.inline_comments[entry])
2040             
2041             if isinstance(this_entry, dict):
2042                 # a section
2043                 out.append(self._write_marker(
2044                     indent_string,
2045                     this_entry.depth,
2046                     entry,
2047                     comment))
2048                 out.extend(self.write(section=this_entry))
2049             else:
2050                 out.append(self._write_line(
2051                     indent_string,
2052                     entry,
2053                     this_entry,
2054                     comment))
2055                 
2056         if section is self:
2057             for line in self.final_comment:
2058                 line = self._decode_element(line)
2059                 stripped_line = line.strip()
2060                 if stripped_line and not stripped_line.startswith(cs):
2061                     line = csp + line
2062                 out.append(line)
2063             self.interpolation = int_val
2064             
2065         if section is not self:
2066             return out
2067         
2068         if (self.filename is None) and (outfile is None):
2069             # output a list of lines
2070             # might need to encode
2071             # NOTE: This will *screw* UTF16, each line will start with the BOM
2072             if self.encoding:
2073                 out = [l.encode(self.encoding) for l in out]
2074             if (self.BOM and ((self.encoding is None) or
2075                 (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2076                 # Add the UTF8 BOM
2077                 if not out:
2078                     out.append('')
2079                 out[0] = BOM_UTF8 + out[0]
2080             return out
2081         
2082         # Turn the list to a string, joined with correct newlines
2083         newline = self.newlines or os.linesep
2084         output = self._a_to_u(newline).join(out)
2085         if self.encoding:
2086             output = output.encode(self.encoding)
2087         if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2088             # Add the UTF8 BOM
2089             output = BOM_UTF8 + output
2090             
2091         if not output.endswith(newline):
2092             output += newline
2093         if outfile is not None:
2094             outfile.write(output)
2095         else:
2096             h = open(self.filename, 'wb')
2097             h.write(output)
2098             h.close()
2099
2100
2101     def validate(self, validator, preserve_errors=False, copy=False,
2102                  section=None):
2103         """
2104         Test the ConfigObj against a configspec.
2105         
2106         It uses the ``validator`` object from *validate.py*.
2107         
2108         To run ``validate`` on the current ConfigObj, call: ::
2109         
2110             test = config.validate(validator)
2111         
2112         (Normally having previously passed in the configspec when the ConfigObj
2113         was created - you can dynamically assign a dictionary of checks to the
2114         ``configspec`` attribute of a section though).
2115         
2116         It returns ``True`` if everything passes, or a dictionary of
2117         pass/fails (True/False). If every member of a subsection passes, it
2118         will just have the value ``True``. (It also returns ``False`` if all
2119         members fail).
2120         
2121         In addition, it converts the values from strings to their native
2122         types if their checks pass (and ``stringify`` is set).
2123         
2124         If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2125         of a marking a fail with a ``False``, it will preserve the actual
2126         exception object. This can contain info about the reason for failure.
2127         For example the ``VdtValueTooSmallError`` indicates that the value
2128         supplied was too small. If a value (or section) is missing it will
2129         still be marked as ``False``.
2130         
2131         You must have the validate module to use ``preserve_errors=True``.
2132         
2133         You can then use the ``flatten_errors`` function to turn your nested
2134         results dictionary into a flattened list of failures - useful for
2135         displaying meaningful error messages.
2136         """
2137         if section is None:
2138             if self.configspec is None:
2139                 raise ValueError('No configspec supplied.')
2140             if preserve_errors:
2141                 # We do this once to remove a top level dependency on the validate module
2142                 # Which makes importing configobj faster
2143                 from validate import VdtMissingValue
2144                 self._vdtMissingValue = VdtMissingValue
2145                 
2146             section = self
2147
2148             if copy:
2149                 section.initial_comment = section.configspec.initial_comment
2150                 section.final_comment = section.configspec.final_comment
2151                 section.encoding = section.configspec.encoding
2152                 section.BOM = section.configspec.BOM
2153                 section.newlines = section.configspec.newlines
2154                 section.indent_type = section.configspec.indent_type
2155             
2156         #
2157         # section.default_values.clear() #??
2158         configspec = section.configspec
2159         self._set_configspec(section, copy)
2160
2161         
2162         def validate_entry(entry, spec, val, missing, ret_true, ret_false):
2163             section.default_values.pop(entry, None)
2164                 
2165             try:
2166                 section.default_values[entry] = validator.get_default_value(configspec[entry])
2167             except (KeyError, AttributeError, validator.baseErrorClass):
2168                 # No default, bad default or validator has no 'get_default_value'
2169                 # (e.g. SimpleVal)
2170                 pass
2171             
2172             try:
2173                 check = validator.check(spec,
2174                                         val,
2175                                         missing=missing
2176                                         )
2177             except validator.baseErrorClass, e:
2178                 if not preserve_errors or isinstance(e, self._vdtMissingValue):
2179                     out[entry] = False
2180                 else:
2181                     # preserve the error
2182                     out[entry] = e
2183                     ret_false = False
2184                 ret_true = False
2185             else:
2186                 ret_false = False
2187                 out[entry] = True
2188                 if self.stringify or missing:
2189                     # if we are doing type conversion
2190                     # or the value is a supplied default
2191                     if not self.stringify:
2192                         if isinstance(check, (list, tuple)):
2193                             # preserve lists
2194                             check = [self._str(item) for item in check]
2195                         elif missing and check is None:
2196                             # convert the None from a default to a ''
2197                             check = ''
2198                         else:
2199                             check = self._str(check)
2200                     if (check != val) or missing:
2201                         section[entry] = check
2202                 if not copy and missing and entry not in section.defaults:
2203                     section.defaults.append(entry)
2204             return ret_true, ret_false
2205         
2206         #
2207         out = {}
2208         ret_true = True
2209         ret_false = True
2210         
2211         unvalidated = [k for k in section.scalars if k not in configspec]
2212         incorrect_sections = [k for k in configspec.sections if k in section.scalars]        
2213         incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2214         
2215         for entry in configspec.scalars:
2216             if entry in ('__many__', '___many___'):
2217                 # reserved names
2218                 continue
2219             if (not entry in section.scalars) or (entry in section.defaults):
2220                 # missing entries
2221                 # or entries from defaults
2222                 missing = True
2223                 val = None
2224                 if copy and entry not in section.scalars:
2225                     # copy comments
2226                     section.comments[entry] = (
2227                         configspec.comments.get(entry, []))
2228                     section.inline_comments[entry] = (
2229                         configspec.inline_comments.get(entry, ''))
2230                 #
2231             else:
2232                 missing = False
2233                 val = section[entry]
2234             
2235             ret_true, ret_false = validate_entry(entry, configspec[entry], val, 
2236                                                  missing, ret_true, ret_false)
2237         
2238         many = None
2239         if '__many__' in configspec.scalars:
2240             many = configspec['__many__']
2241         elif '___many___' in configspec.scalars:
2242             many = configspec['___many___']
2243         
2244         if many is not None:
2245             for entry in unvalidated:
2246                 val = section[entry]
2247                 ret_true, ret_false = validate_entry(entry, many, val, False,
2248                                                      ret_true, ret_false)
2249             unvalidated = []
2250
2251         for entry in incorrect_scalars:
2252             ret_true = False
2253             if not preserve_errors:
2254                 out[entry] = False
2255             else:
2256                 ret_false = False
2257                 msg = 'Value %r was provided as a section' % entry
2258                 out[entry] = validator.baseErrorClass(msg)
2259         for entry in incorrect_sections:
2260             ret_true = False
2261             if not preserve_errors:
2262                 out[entry] = False
2263             else:
2264                 ret_false = False
2265                 msg = 'Section %r was provided as a single value' % entry
2266                 out[entry] = validator.baseErrorClass(msg)
2267                 
2268         # Missing sections will have been created as empty ones when the
2269         # configspec was read.
2270         for entry in section.sections:
2271             # FIXME: this means DEFAULT is not copied in copy mode
2272             if section is self and entry == 'DEFAULT':
2273                 continue
2274             if section[entry].configspec is None:
2275                 unvalidated.append(entry)
2276                 continue
2277             if copy:
2278                 section.comments[entry] = configspec.comments.get(entry, [])
2279                 section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
2280             check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
2281             out[entry] = check
2282             if check == False:
2283                 ret_true = False
2284             elif check == True:
2285                 ret_false = False
2286             else:
2287                 ret_true = False
2288         
2289         section.extra_values = unvalidated
2290         if preserve_errors and not section._created:
2291             # If the section wasn't created (i.e. it wasn't missing)
2292             # then we can't return False, we need to preserve errors
2293             ret_false = False
2294         #
2295         if ret_false and preserve_errors and out:
2296             # If we are preserving errors, but all
2297             # the failures are from missing sections / values
2298             # then we can return False. Otherwise there is a
2299             # real failure that we need to preserve.
2300             ret_false = not any(out.values())
2301         if ret_true:
2302             return True
2303         elif ret_false:
2304             return False
2305         return out
2306
2307
2308     def reset(self):
2309         """Clear ConfigObj instance and restore to 'freshly created' state."""
2310         self.clear()
2311         self._initialise()
2312         # FIXME: Should be done by '_initialise', but ConfigObj constructor (and reload)
2313         #        requires an empty dictionary
2314         self.configspec = None
2315         # Just to be sure ;-)
2316         self._original_configspec = None
2317         
2318         
2319     def reload(self):
2320         """
2321         Reload a ConfigObj from file.
2322         
2323         This method raises a ``ReloadError`` if the ConfigObj doesn't have
2324         a filename attribute pointing to a file.
2325         """
2326         if not isinstance(self.filename, basestring):
2327             raise ReloadError()
2328
2329         filename = self.filename
2330         current_options = {}
2331         for entry in OPTION_DEFAULTS:
2332             if entry == 'configspec':
2333                 continue
2334             current_options[entry] = getattr(self, entry)
2335             
2336         configspec = self._original_configspec
2337         current_options['configspec'] = configspec
2338             
2339         self.clear()
2340         self._initialise(current_options)
2341         self._load(filename, configspec)
2342         
2343
2344
2345 class SimpleVal(object):
2346     """
2347     A simple validator.
2348     Can be used to check that all members expected are present.
2349     
2350     To use it, provide a configspec with all your members in (the value given
2351     will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2352     method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2353     members are present, or a dictionary with True/False meaning
2354     present/missing. (Whole missing sections will be replaced with ``False``)
2355     """
2356     
2357     def __init__(self):
2358         self.baseErrorClass = ConfigObjError
2359     
2360     def check(self, check, member, missing=False):
2361         """A dummy check method, always returns the value unchanged."""
2362         if missing:
2363             raise self.baseErrorClass()
2364         return member
2365
2366
2367 def flatten_errors(cfg, res, levels=None, results=None):
2368     """
2369     An example function that will turn a nested dictionary of results
2370     (as returned by ``ConfigObj.validate``) into a flat list.
2371     
2372     ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2373     dictionary returned by ``validate``.
2374     
2375     (This is a recursive function, so you shouldn't use the ``levels`` or
2376     ``results`` arguments - they are used by the function.)
2377     
2378     Returns a list of keys that failed. Each member of the list is a tuple::
2379     
2380         ([list of sections...], key, result)
2381     
2382     If ``validate`` was called with ``preserve_errors=False`` (the default)
2383     then ``result`` will always be ``False``.
2384
2385     *list of sections* is a flattened list of sections that the key was found
2386     in.
2387     
2388     If the section was missing (or a section was expected and a scalar provided
2389     - or vice-versa) then key will be ``None``.
2390     
2391     If the value (or section) was missing then ``result`` will be ``False``.
2392     
2393     If ``validate`` was called with ``preserve_errors=True`` and a value
2394     was present, but failed the check, then ``result`` will be the exception
2395     object returned. You can use this as a string that describes the failure.
2396     
2397     For example *The value "3" is of the wrong type*.
2398     """
2399     if levels is None:
2400         # first time called
2401         levels = []
2402         results = []
2403     if res == True:
2404         return results
2405     if res == False or isinstance(res, Exception):
2406         results.append((levels[:], None, res))
2407         if levels:
2408             levels.pop()
2409         return results
2410     for (key, val) in res.items():
2411         if val == True:
2412             continue
2413         if isinstance(cfg.get(key), dict):
2414             # Go down one level
2415             levels.append(key)
2416             flatten_errors(cfg[key], val, levels, results)
2417             continue
2418         results.append((levels[:], key, val))
2419     #
2420     # Go up one level
2421     if levels:
2422         levels.pop()
2423     #
2424     return results
2425
2426
2427 def get_extra_values(conf, _prepend=()):
2428     """
2429     Find all the values and sections not in the configspec from a validated
2430     ConfigObj.
2431     
2432     ``get_extra_values`` returns a list of tuples where each tuple represents
2433     either an extra section, or an extra value.
2434     
2435     The tuples contain two values, a tuple representing the section the value 
2436     is in and the name of the extra values. For extra values in the top level
2437     section the first member will be an empty tuple. For values in the 'foo'
2438     section the first member will be ``('foo',)``. For members in the 'bar'
2439     subsection of the 'foo' section the first member will be ``('foo', 'bar')``.
2440     
2441     NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't
2442     been validated it will return an empty list.
2443     """
2444     out = []
2445     
2446     out.extend((_prepend, name) for name in conf.extra_values)
2447     for name in conf.sections:
2448         if name not in conf.extra_values:
2449             out.extend(get_extra_values(conf[name], _prepend + (name,)))
2450     return out
2451
2452
2453 """*A programming language is a medium of expression.* - Paul Graham"""