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