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