configobj

Module configobj |
[hide private]
[frames] | no frames]

# Source Code for Module configobj

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