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