1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
39
40 BOM_UTF8 = '\xef\xbb\xbf'
41
42 BOM_UTF16_LE = '\xff\xfe'
43
44 BOM_UTF16_BE = '\xfe\xff'
45 if sys.byteorder == 'little':
46
47 BOM_UTF16 = BOM_UTF16_LE
48 else:
49
50 BOM_UTF16 = BOM_UTF16_BE
51
52
53
54
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
62
63
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
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
94
95
96
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:
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
161 'indent_type': None,
162 'encoding': None,
163 'default_encoding': None,
164 'unrepr': False,
165 'write_empty_values': False,
166 }
167
168
169
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
180
181
183
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
191 return map(self.build, o.getChildren())
192
195
197 d = {}
198 i = iter(map(self.build, o.getChildren()))
199 for el in i:
200 d[el] = i.next()
201 return d
202
205
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
215 raise UnknownType('Undefined Name')
216
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
228 parent = self.build(o.expr)
229 return getattr(parent, o.attrname)
230
233
236
237
238 _builder = Builder()
239
240
245
246
247
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
261 """
262 This error indicates a level of nesting that doesn't match.
263 """
264
265
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
275 """
276 A 'reload' operation failed.
277 This exception is a subclass of ``IOError``.
278 """
280 IOError.__init__(self, 'reload failed, filename is not set.')
281
282
284 """
285 The keyword or section specified already exists.
286 """
287
288
290 """
291 An error occured whilst parsing a configspec.
292 """
293
294
296 """Base class for the two interpolation errors."""
297
298
300 """Maximum interpolation depth exceeded in string interpolation."""
301
306
307
309 """
310 This error indicates additional sections in a section with a
311 ``__many__`` (repeated) section.
312 """
313
314
316 """A value specified for interpolation was missing."""
317
322
323
325 """An error parsing in unrepr mode."""
326
327
328
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
338 _KEYCRE = re.compile(r"%\(([^)]*)\)s")
339
341
342 self.section = section
343
344
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
357 if backtrail.has_key((key, section.name)):
358
359 raise InterpolationLoopError(key)
360
361 backtrail[(key, section.name)] = 1
362
363
364 match = self._KEYCRE.search(value)
365 while match:
366
367
368 k, v, s = self._parse_match(match)
369 if k is None:
370
371 replacement = v
372 else:
373
374 replacement = recursive_interpolate(k, v, s, backtrail)
375
376 start, end = match.span()
377 value = ''.join((value[:start], replacement, value[end:]))
378 new_search_start = start + len(replacement)
379
380
381 match = self._KEYCRE.search(value, new_search_start)
382
383
384 del backtrail[(key, section.name)]
385
386 return value
387
388
389
390 value = recursive_interpolate(key, value, self.section, {})
391 return value
392
393
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
400 save_interp = self.section.main.interpolation
401 self.section.main.interpolation = False
402
403
404 current_section = self.section
405 while True:
406
407 val = current_section.get(key)
408 if val is not None:
409 break
410
411 val = current_section.get('DEFAULT', {}).get(key)
412 if val is not None:
413 break
414
415
416 if current_section.parent is current_section:
417
418 break
419 current_section = current_section.parent
420
421
422 self.section.main.interpolation = save_interp
423 if val is None:
424 raise MissingInterpolationOption(key)
425 return val, current_section
426
427
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
449 """Behaves like ConfigParser."""
450 _KEYCRE = re.compile(r"%\(([^)]*)\)s")
451
453 key = match.group(1)
454 value, section = self._fetch(key)
455 return key, value, section
456
457
458
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
471
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
477 if match.group('escaped') is not None:
478
479 return None, self._delimiter, None
480
481 return None, match.group(), None
482
483
484 interpolation_engines = {
485 'configparser': ConfigParserInterpolation,
486 'template': TemplateInterpolation,
487 }
488
489
490
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
520 self.parent = parent
521
522 self.main = main
523
524 self.depth = depth
525
526 self.name = name
527
528 self._initialise()
529
530
531 for entry, value in indict.iteritems():
532 self[entry] = value
533
534
536
537 self.scalars = []
538
539 self.sections = []
540
541 self.comments = {}
542 self.inline_comments = {}
543
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
551 self.defaults = []
552 self.default_values = {}
553
554
556 try:
557
558 engine = self._interpolation_engine
559 except AttributeError:
560
561 name = self.main.interpolation
562 if name == True:
563
564 name = DEFAULT_INTERPOLATION
565 name = name.lower()
566 class_ = interpolation_engines.get(name, None)
567 if class_ is None:
568
569 self.main.interpolation = False
570 return value
571 else:
572
573 engine = self._interpolation_engine = class_(self)
574
575 return engine.interpolate(key, value)
576
577
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
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
604 if not self.comments.has_key(key):
605 self.comments[key] = []
606 self.inline_comments[key] = ''
607
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
617
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
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
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
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
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
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
728 """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
729 return zip((self.scalars + self.sections), self.values())
730
731
733 """D.keys() -> list of D's keys"""
734 return (self.scalars + self.sections)
735
736
738 """D.values() -> list of D's values"""
739 return [self[key] for key in (self.scalars + self.sections)]
740
741
743 """D.iteritems() -> an iterator over the (key, value) items of D"""
744 return iter(self.items())
745
746
748 """D.iterkeys() -> an iterator over the keys of D"""
749 return iter((self.scalars + self.sections))
750
751 __iter__ = iterkeys
752
753
755 """D.itervalues() -> an iterator over the values of D"""
756 return iter(self.values())
757
758
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
769
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
790 this_entry = list(this_entry)
791 elif isinstance(this_entry, tuple):
792
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
904 for i in range(len(self.scalars)):
905 entry = self.scalars[i]
906 try:
907 val = function(self, entry, **keywargs)
908
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
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
930 entry = self.sections[i]
931
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
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
981 self.walk(decode, call_on_sections=True)
982
983
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
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
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
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
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
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
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
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
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
1169
1170
1171
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
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
1210
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
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
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
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
1259 options.update(kwargs)
1260
1261 defaults = OPTION_DEFAULTS.copy()
1262
1263 for entry in options:
1264 if entry not in defaults:
1265 raise TypeError('Unrecognised option "%s".' % entry)
1266
1267
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
1284 raise IOError('Config file not found: "%s".' % self.filename)
1285 else:
1286
1287 if self.create_empty:
1288
1289
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
1300
1301 if isinstance(infile, ConfigObj):
1302
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
1317 infile = infile.read() or []
1318
1319
1320 else:
1321 raise TypeError('infile must be a filename, file like object, or list of lines.')
1322
1323 if infile:
1324
1325 infile = self._handle_bom(infile)
1326
1327
1328
1329
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
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
1351
1352 error.errors = self._errors
1353
1354 error.config = self
1355 raise error
1356
1357 del self._errors
1358
1359 if configspec is None:
1360 self.configspec = None
1361 else:
1362 self._handle_configspec(configspec)
1363
1364
1366 if options is None:
1367 options = OPTION_DEFAULTS
1368
1369
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
1391 Section._initialise(self)
1392
1393
1395 return ('ConfigObj({%s})' %
1396 ', '.join([('%s: %s' % (repr(key), repr(self[key])))
1397 for key in (self.scalars + self.sections)]))
1398
1399
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
1425
1426
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
1435
1436
1437
1438 enc = BOM_LIST[self.encoding.lower()]
1439 if enc == 'utf_16':
1440
1441 for BOM, (encoding, final_encoding) in BOMS.items():
1442 if not final_encoding:
1443
1444 continue
1445 if infile.startswith(BOM):
1446
1447
1448
1449 return self._decode(infile, encoding)
1450
1451
1452
1453 return self._decode(infile, self.encoding)
1454
1455
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
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
1471 for BOM, (encoding, final_encoding) in BOMS.items():
1472 if not line.startswith(BOM):
1473 continue
1474 else:
1475
1476 self.encoding = final_encoding
1477 if not final_encoding:
1478 self.BOM = True
1479
1480
1481 newline = line[len(BOM):]
1482 if isinstance(infile, (list, tuple)):
1483 infile[0] = newline
1484 else:
1485 infile = newline
1486
1487 if isinstance(infile, StringTypes):
1488 return infile.splitlines(True)
1489 else:
1490 return infile
1491
1492 return self._decode(infile, encoding)
1493
1494
1495 if isinstance(infile, StringTypes):
1496
1497 return infile.splitlines(True)
1498 return infile
1499
1500
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
1517
1518 return infile.decode(encoding).splitlines(True)
1519 for i, line in enumerate(infile):
1520 if not isinstance(line, unicode):
1521
1522
1523
1524 infile[i] = line.decode(encoding)
1525 return infile
1526
1527
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
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
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
1575 self.initial_comment = comment_list
1576 comment_list = []
1577 done_start = True
1578
1579 reset_comment = True
1580
1581 mat = self._sectionmarker.match(line)
1582 if mat is not None:
1583
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
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
1604 parent = this_section.parent
1605 elif cur_depth == this_section.depth + 1:
1606
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
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
1630
1631 mat = self._keyword.match(line)
1632 if mat is None:
1633
1634
1635 self._handle_error(
1636 'Invalid line at line "%s".',
1637 ParseError, infile, cur_index)
1638 else:
1639
1640
1641 (indent, key, value) = mat.groups()
1642 if indent and (self.indent_type is None):
1643 self.indent_type = indent
1644
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
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
1697
1698
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
1706 self.indent_type = ''
1707
1708
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
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
1727 raise SyntaxError()
1728 sect = sect.parent
1729 if sect.depth == depth:
1730 return sect
1731
1732 raise SyntaxError()
1733
1734
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
1748 raise error
1749
1750
1751 self._errors.append(error)
1752
1753
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
1782
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
1809 quot = noquot
1810
1811 elif '\n' in value:
1812
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
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
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
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
1851 """
1852 Given a value string, unquote, remove comment,
1853 handle lists. (including empty and single member lists)
1854 """
1855
1856 if not self.list_values:
1857 mat = self._nolistvalue.match(value)
1858 if mat is None:
1859 raise SyntaxError()
1860
1861 return mat.groups()
1862
1863 mat = self._valueexp.match(value)
1864 if mat is None:
1865
1866
1867 raise SyntaxError()
1868 (list_values, single, empty_list, comment) = mat.groups()
1869 if (list_values == '') and (single is None):
1870
1871 raise SyntaxError()
1872
1873
1874 if empty_list is not None:
1875
1876 return ([], comment)
1877 if single is not None:
1878
1879 if list_values and not single:
1880
1881
1882 single = None
1883 else:
1884 single = single or '""'
1885 single = self._unquote(single)
1886 if list_values == '':
1887
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
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
1919 break
1920 else:
1921
1922 raise SyntaxError()
1923 mat = multi_line.match(line)
1924 if mat is None:
1925
1926 raise SyntaxError()
1927 (value, comment) = mat.groups()
1928 return (newvalue + value, comment, cur_index)
1929
1930
1932 """Parse the configspec."""
1933
1934
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
1943
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
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
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
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
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
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
2039
2040
2049
2050
2051
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
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
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
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
2126
2127
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
2133 if not out:
2134 out.append('')
2135 out[0] = BOM_UTF8 + out[0]
2136 return out
2137
2138
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
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
2198
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
2215
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
2229
2230 missing = True
2231 val = None
2232 if copy and not entry in section.scalars:
2233
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
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
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
2270 pass
2271
2272 ret_false = False
2273 out[entry] = True
2274 if self.stringify or missing:
2275
2276
2277 if not self.stringify:
2278 if isinstance(check, (list, tuple)):
2279
2280 check = [self._str(item) for item in check]
2281 elif missing and check is None:
2282
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
2291
2292 for entry in section.sections:
2293
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
2319 """Clear ConfigObj instance and restore to 'freshly created' state."""
2320 self.clear()
2321 self._initialise()
2322
2323
2324 self.configspec = None
2325
2326 self._original_configspec = None
2327
2328
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
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
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
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
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
2489 levels.append(key)
2490 flatten_errors(cfg[key], val, levels, results)
2491 continue
2492 results.append((levels[:], key, val))
2493
2494
2495 if levels:
2496 levels.pop()
2497
2498 return results
2499
2500
2501 """*A programming language is a medium of expression.* - Paul Graham"""
2502