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