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.2'
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 and not isinstance(val, Section):
384 break
385
386 val = current_section.get('DEFAULT', {}).get(key)
387 if val is not None and not isinstance(val, Section):
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 new = [_check(entry) for entry in val]
577 if new != val:
578 return new
579 return val
580
581
583 """
584 Correctly set a value.
585
586 Making dictionary values Section instances.
587 (We have to special case 'Section' instances - which are also dicts)
588
589 Keys must be strings.
590 Values need only be strings (or lists of strings) if
591 ``main.stringify`` is set.
592
593 ``unrepr`` must be set when setting a value to a dictionary, without
594 creating a new sub-section.
595 """
596 if not isinstance(key, basestring):
597 raise ValueError('The key "%s" is not a string.' % key)
598
599
600 if key not in self.comments:
601 self.comments[key] = []
602 self.inline_comments[key] = ''
603
604 if key in self.defaults:
605 self.defaults.remove(key)
606
607 if isinstance(value, Section):
608 if key not in self:
609 self.sections.append(key)
610 dict.__setitem__(self, key, value)
611 elif isinstance(value, dict) and not unrepr:
612
613
614 if key not in self:
615 self.sections.append(key)
616 new_depth = self.depth + 1
617 dict.__setitem__(
618 self,
619 key,
620 Section(
621 self,
622 new_depth,
623 self.main,
624 indict=value,
625 name=key))
626 else:
627 if key not in self:
628 self.scalars.append(key)
629 if not self.main.stringify:
630 if isinstance(value, basestring):
631 pass
632 elif isinstance(value, (list, tuple)):
633 for entry in value:
634 if not isinstance(entry, basestring):
635 raise TypeError('Value is not a string "%s".' % entry)
636 else:
637 raise TypeError('Value is not a string "%s".' % value)
638 dict.__setitem__(self, key, value)
639
640
642 """Remove items from the sequence when deleting."""
643 dict. __delitem__(self, key)
644 if key in self.scalars:
645 self.scalars.remove(key)
646 else:
647 self.sections.remove(key)
648 del self.comments[key]
649 del self.inline_comments[key]
650
651
652 - def get(self, key, default=None):
653 """A version of ``get`` that doesn't bypass string interpolation."""
654 try:
655 return self[key]
656 except KeyError:
657 return default
658
659
661 """
662 A version of update that uses our ``__setitem__``.
663 """
664 for entry in indict:
665 self[entry] = indict[entry]
666
667
669 """
670 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
671 If key is not found, d is returned if given, otherwise KeyError is raised'
672 """
673 try:
674 val = self[key]
675 except KeyError:
676 if default is MISSING:
677 raise
678 val = default
679 else:
680 del self[key]
681 return val
682
683
685 """Pops the first (key,val)"""
686 sequence = (self.scalars + self.sections)
687 if not sequence:
688 raise KeyError(": 'popitem(): dictionary is empty'")
689 key = sequence[0]
690 val = self[key]
691 del self[key]
692 return key, val
693
694
696 """
697 A version of clear that also affects scalars/sections
698 Also clears comments and configspec.
699
700 Leaves other attributes alone :
701 depth/main/parent are not affected
702 """
703 dict.clear(self)
704 self.scalars = []
705 self.sections = []
706 self.comments = {}
707 self.inline_comments = {}
708 self.configspec = None
709 self.defaults = []
710 self.extra_values = []
711
712
714 """A version of setdefault that sets sequence if appropriate."""
715 try:
716 return self[key]
717 except KeyError:
718 self[key] = default
719 return self[key]
720
721
723 """D.items() -> list of D's (key, value) pairs, as 2-tuples"""
724 return zip((self.scalars + self.sections), self.values())
725
726
728 """D.keys() -> list of D's keys"""
729 return (self.scalars + self.sections)
730
731
733 """D.values() -> list of D's values"""
734 return [self[key] for key in (self.scalars + self.sections)]
735
736
738 """D.iteritems() -> an iterator over the (key, value) items of D"""
739 return iter(self.items())
740
741
743 """D.iterkeys() -> an iterator over the keys of D"""
744 return iter((self.scalars + self.sections))
745
746 __iter__ = iterkeys
747
748
750 """D.itervalues() -> an iterator over the values of D"""
751 return iter(self.values())
752
753
761 return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(_getval(key))))
762 for key in (self.scalars + self.sections)])
763
764 __str__ = __repr__
765 __str__.__doc__ = "x.__str__() <==> str(x)"
766
767
768
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 ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}})
816 """
817 for key, val in indict.items():
818 if (key in self and isinstance(self[key], dict) and
819 isinstance(val, dict)):
820 self[key].merge(val)
821 else:
822 self[key] = val
823
824
825 - def rename(self, oldkey, newkey):
826 """
827 Change a keyname to another, without changing position in sequence.
828
829 Implemented so that transformations can be made on keys,
830 as well as on values. (used by encode and decode)
831
832 Also renames comments.
833 """
834 if oldkey in self.scalars:
835 the_list = self.scalars
836 elif oldkey in self.sections:
837 the_list = self.sections
838 else:
839 raise KeyError('Key "%s" not found.' % oldkey)
840 pos = the_list.index(oldkey)
841
842 val = self[oldkey]
843 dict.__delitem__(self, oldkey)
844 dict.__setitem__(self, newkey, val)
845 the_list.remove(oldkey)
846 the_list.insert(pos, newkey)
847 comm = self.comments[oldkey]
848 inline_comment = self.inline_comments[oldkey]
849 del self.comments[oldkey]
850 del self.inline_comments[oldkey]
851 self.comments[newkey] = comm
852 self.inline_comments[newkey] = inline_comment
853
854
855 - def walk(self, function, raise_errors=True,
856 call_on_sections=False, **keywargs):
857 """
858 Walk every member and call a function on the keyword and value.
859
860 Return a dictionary of the return values
861
862 If the function raises an exception, raise the errror
863 unless ``raise_errors=False``, in which case set the return value to
864 ``False``.
865
866 Any unrecognised keyword arguments you pass to walk, will be pased on
867 to the function you pass in.
868
869 Note: if ``call_on_sections`` is ``True`` then - on encountering a
870 subsection, *first* the function is called for the *whole* subsection,
871 and then recurses into it's members. This means your function must be
872 able to handle strings, dictionaries and lists. This allows you
873 to change the key of subsections as well as for ordinary members. The
874 return value when called on the whole subsection has to be discarded.
875
876 See the encode and decode methods for examples, including functions.
877
878 .. admonition:: caution
879
880 You can use ``walk`` to transform the names of members of a section
881 but you mustn't add or delete members.
882
883 >>> config = '''[XXXXsection]
884 ... XXXXkey = XXXXvalue'''.splitlines()
885 >>> cfg = ConfigObj(config)
886 >>> cfg
887 ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}})
888 >>> def transform(section, key):
889 ... val = section[key]
890 ... newkey = key.replace('XXXX', 'CLIENT1')
891 ... section.rename(key, newkey)
892 ... if isinstance(val, (tuple, list, dict)):
893 ... pass
894 ... else:
895 ... val = val.replace('XXXX', 'CLIENT1')
896 ... section[newkey] = val
897 >>> cfg.walk(transform, call_on_sections=True)
898 {'CLIENT1section': {'CLIENT1key': None}}
899 >>> cfg
900 ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}})
901 """
902 out = {}
903
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 Accepts a key as input. The corresponding value must be a string or
943 the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
944 retain compatibility with Python 2.2.
945
946 If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
947 ``True``.
948
949 If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
950 ``False``.
951
952 ``as_bool`` is not case sensitive.
953
954 Any other input will raise a ``ValueError``.
955
956 >>> a = ConfigObj()
957 >>> a['a'] = 'fish'
958 >>> a.as_bool('a')
959 Traceback (most recent call last):
960 ValueError: Value "fish" is neither True nor False
961 >>> a['b'] = 'True'
962 >>> a.as_bool('b')
963 1
964 >>> a['b'] = 'off'
965 >>> a.as_bool('b')
966 0
967 """
968 val = self[key]
969 if val == True:
970 return True
971 elif val == False:
972 return False
973 else:
974 try:
975 if not isinstance(val, basestring):
976
977 raise KeyError()
978 else:
979 return self.main._bools[val.lower()]
980 except KeyError:
981 raise ValueError('Value "%s" is neither True nor False' % val)
982
983
985 """
986 A convenience method which coerces the specified value to an integer.
987
988 If the value is an invalid literal for ``int``, a ``ValueError`` will
989 be raised.
990
991 >>> a = ConfigObj()
992 >>> a['a'] = 'fish'
993 >>> a.as_int('a')
994 Traceback (most recent call last):
995 ValueError: invalid literal for int() with base 10: 'fish'
996 >>> a['b'] = '1'
997 >>> a.as_int('b')
998 1
999 >>> a['b'] = '3.2'
1000 >>> a.as_int('b')
1001 Traceback (most recent call last):
1002 ValueError: invalid literal for int() with base 10: '3.2'
1003 """
1004 return int(self[key])
1005
1006
1008 """
1009 A convenience method which coerces the specified value to a float.
1010
1011 If the value is an invalid literal for ``float``, a ``ValueError`` will
1012 be raised.
1013 """
1014 return float(self[key])
1015
1016
1018 """
1019 A convenience method which fetches the specified value, guaranteeing
1020 that it is a list.
1021
1022 >>> a = ConfigObj()
1023 >>> a['a'] = 1
1024 >>> a.as_list('a')
1025 [1]
1026 >>> a['a'] = (1,)
1027 >>> a.as_list('a')
1028 [1]
1029 >>> a['a'] = [1]
1030 >>> a.as_list('a')
1031 [1]
1032 """
1033 result = self[key]
1034 if isinstance(result, (tuple, list)):
1035 return list(result)
1036 return [result]
1037
1038
1040 """
1041 Restore (and return) default value for the specified key.
1042
1043 This method will only work for a ConfigObj that was created
1044 with a configspec and has been validated.
1045
1046 If there is no default value for this key, ``KeyError`` is raised.
1047 """
1048 default = self.default_values[key]
1049 dict.__setitem__(self, key, default)
1050 if key not in self.defaults:
1051 self.defaults.append(key)
1052 return default
1053
1054
1056 """
1057 Recursively restore default values to all members
1058 that have them.
1059
1060 This method will only work for a ConfigObj that was created
1061 with a configspec and has been validated.
1062
1063 It doesn't delete or modify entries without default values.
1064 """
1065 for key in self.default_values:
1066 self.restore_default(key)
1067
1068 for section in self.sections:
1069 self[section].restore_defaults()
1070
1071
1073 """An object to read, create, and write config files."""
1074
1075 _keyword = re.compile(r'''^ # line start
1076 (\s*) # indentation
1077 ( # keyword
1078 (?:".*?")| # double quotes
1079 (?:'.*?')| # single quotes
1080 (?:[^'"=].*?) # no quotes
1081 )
1082 \s*=\s* # divider
1083 (.*) # value (including list values and comments)
1084 $ # line end
1085 ''',
1086 re.VERBOSE)
1087
1088 _sectionmarker = re.compile(r'''^
1089 (\s*) # 1: indentation
1090 ((?:\[\s*)+) # 2: section marker open
1091 ( # 3: section name open
1092 (?:"\s*\S.*?\s*")| # at least one non-space with double quotes
1093 (?:'\s*\S.*?\s*')| # at least one non-space with single quotes
1094 (?:[^'"\s].*?) # at least one non-space unquoted
1095 ) # section name close
1096 ((?:\s*\])+) # 4: section marker close
1097 \s*(\#.*)? # 5: optional comment
1098 $''',
1099 re.VERBOSE)
1100
1101
1102
1103
1104
1105 _valueexp = re.compile(r'''^
1106 (?:
1107 (?:
1108 (
1109 (?:
1110 (?:
1111 (?:".*?")| # double quotes
1112 (?:'.*?')| # single quotes
1113 (?:[^'",\#][^,\#]*?) # unquoted
1114 )
1115 \s*,\s* # comma
1116 )* # match all list items ending in a comma (if any)
1117 )
1118 (
1119 (?:".*?")| # double quotes
1120 (?:'.*?')| # single quotes
1121 (?:[^'",\#\s][^,]*?)| # unquoted
1122 (?:(?<!,)) # Empty value
1123 )? # last item in a list - or string value
1124 )|
1125 (,) # alternatively a single comma - empty list
1126 )
1127 \s*(\#.*)? # optional comment
1128 $''',
1129 re.VERBOSE)
1130
1131
1132 _listvalueexp = re.compile(r'''
1133 (
1134 (?:".*?")| # double quotes
1135 (?:'.*?')| # single quotes
1136 (?:[^'",\#]?.*?) # unquoted
1137 )
1138 \s*,\s* # comma
1139 ''',
1140 re.VERBOSE)
1141
1142
1143
1144 _nolistvalue = re.compile(r'''^
1145 (
1146 (?:".*?")| # double quotes
1147 (?:'.*?')| # single quotes
1148 (?:[^'"\#].*?)| # unquoted
1149 (?:) # Empty value
1150 )
1151 \s*(\#.*)? # optional comment
1152 $''',
1153 re.VERBOSE)
1154
1155
1156 _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
1157 _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
1158 _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
1159 _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
1160
1161 _triple_quote = {
1162 "'''": (_single_line_single, _multi_line_single),
1163 '"""': (_single_line_double, _multi_line_double),
1164 }
1165
1166
1167 _bools = {
1168 'yes': True, 'no': False,
1169 'on': True, 'off': False,
1170 '1': True, '0': False,
1171 'true': True, 'false': False,
1172 }
1173
1174
1175 - def __init__(self, infile=None, options=None, configspec=None, encoding=None,
1176 interpolation=True, raise_errors=False, list_values=True,
1177 create_empty=False, file_error=False, stringify=True,
1178 indent_type=None, default_encoding=None, unrepr=False,
1179 write_empty_values=False, _inspec=False):
1180 """
1181 Parse a config file or create a config file object.
1182
1183 ``ConfigObj(infile=None, configspec=None, encoding=None,
1184 interpolation=True, raise_errors=False, list_values=True,
1185 create_empty=False, file_error=False, stringify=True,
1186 indent_type=None, default_encoding=None, unrepr=False,
1187 write_empty_values=False, _inspec=False)``
1188 """
1189 self._inspec = _inspec
1190
1191 Section.__init__(self, self, 0, self)
1192
1193 infile = infile or []
1194
1195 _options = {'configspec': configspec,
1196 'encoding': encoding, 'interpolation': interpolation,
1197 'raise_errors': raise_errors, 'list_values': list_values,
1198 'create_empty': create_empty, 'file_error': file_error,
1199 'stringify': stringify, 'indent_type': indent_type,
1200 'default_encoding': default_encoding, 'unrepr': unrepr,
1201 'write_empty_values': write_empty_values}
1202
1203 if options is None:
1204 options = _options
1205 else:
1206 import warnings
1207 warnings.warn('Passing in an options dictionary to ConfigObj() is '
1208 'deprecated. Use **options instead.',
1209 DeprecationWarning, stacklevel=2)
1210
1211
1212 for entry in options:
1213 if entry not in OPTION_DEFAULTS:
1214 raise TypeError('Unrecognised option "%s".' % entry)
1215 for entry, value in OPTION_DEFAULTS.items():
1216 if entry not in options:
1217 options[entry] = value
1218 keyword_value = _options[entry]
1219 if value != keyword_value:
1220 options[entry] = keyword_value
1221
1222
1223
1224 if _inspec:
1225 options['list_values'] = False
1226
1227 self._initialise(options)
1228 configspec = options['configspec']
1229 self._original_configspec = configspec
1230 self._load(infile, configspec)
1231
1232
1233 - def _load(self, infile, configspec):
1234 if isinstance(infile, basestring):
1235 self.filename = infile
1236 if os.path.isfile(infile):
1237 h = open(infile, 'rb')
1238 infile = h.read() or []
1239 h.close()
1240 elif self.file_error:
1241
1242 raise IOError('Config file not found: "%s".' % self.filename)
1243 else:
1244
1245 if self.create_empty:
1246
1247
1248 h = open(infile, 'w')
1249 h.write('')
1250 h.close()
1251 infile = []
1252
1253 elif isinstance(infile, (list, tuple)):
1254 infile = list(infile)
1255
1256 elif isinstance(infile, dict):
1257
1258
1259 if isinstance(infile, ConfigObj):
1260
1261 def set_section(in_section, this_section):
1262 for entry in in_section.scalars:
1263 this_section[entry] = in_section[entry]
1264 for section in in_section.sections:
1265 this_section[section] = {}
1266 set_section(in_section[section], this_section[section])
1267 set_section(infile, self)
1268
1269 else:
1270 for entry in infile:
1271 self[entry] = infile[entry]
1272 del self._errors
1273
1274 if configspec is not None:
1275 self._handle_configspec(configspec)
1276 else:
1277 self.configspec = None
1278 return
1279
1280 elif getattr(infile, 'read', MISSING) is not MISSING:
1281
1282 infile = infile.read() or []
1283
1284
1285 else:
1286 raise TypeError('infile must be a filename, file like object, or list of lines.')
1287
1288 if infile:
1289
1290 infile = self._handle_bom(infile)
1291
1292
1293
1294
1295 for line in infile:
1296 if (not line) or (line[-1] not in ('\r', '\n', '\r\n')):
1297 continue
1298 for end in ('\r\n', '\n', '\r'):
1299 if line.endswith(end):
1300 self.newlines = end
1301 break
1302 break
1303
1304 infile = [line.rstrip('\r\n') for line in infile]
1305
1306 self._parse(infile)
1307
1308 if self._errors:
1309 info = "at line %s." % self._errors[0].line_number
1310 if len(self._errors) > 1:
1311 msg = "Parsing failed with several errors.\nFirst error %s" % info
1312 error = ConfigObjError(msg)
1313 else:
1314 error = self._errors[0]
1315
1316
1317 error.errors = self._errors
1318
1319 error.config = self
1320 raise error
1321
1322 del self._errors
1323
1324 if configspec is None:
1325 self.configspec = None
1326 else:
1327 self._handle_configspec(configspec)
1328
1329
1331 if options is None:
1332 options = OPTION_DEFAULTS
1333
1334
1335 self.filename = None
1336 self._errors = []
1337 self.raise_errors = options['raise_errors']
1338 self.interpolation = options['interpolation']
1339 self.list_values = options['list_values']
1340 self.create_empty = options['create_empty']
1341 self.file_error = options['file_error']
1342 self.stringify = options['stringify']
1343 self.indent_type = options['indent_type']
1344 self.encoding = options['encoding']
1345 self.default_encoding = options['default_encoding']
1346 self.BOM = False
1347 self.newlines = None
1348 self.write_empty_values = options['write_empty_values']
1349 self.unrepr = options['unrepr']
1350
1351 self.initial_comment = []
1352 self.final_comment = []
1353 self.configspec = None
1354
1355 if self._inspec:
1356 self.list_values = False
1357
1358
1359 Section._initialise(self)
1360
1361
1368 return ('ConfigObj({%s})' %
1369 ', '.join([('%s: %s' % (repr(key), repr(_getval(key))))
1370 for key in (self.scalars + self.sections)]))
1371
1372
1374 """
1375 Handle any BOM, and decode if necessary.
1376
1377 If an encoding is specified, that *must* be used - but the BOM should
1378 still be removed (and the BOM attribute set).
1379
1380 (If the encoding is wrongly specified, then a BOM for an alternative
1381 encoding won't be discovered or removed.)
1382
1383 If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1384 removed. The BOM attribute will be set. UTF16 will be decoded to
1385 unicode.
1386
1387 NOTE: This method must not be called with an empty ``infile``.
1388
1389 Specifying the *wrong* encoding is likely to cause a
1390 ``UnicodeDecodeError``.
1391
1392 ``infile`` must always be returned as a list of lines, but may be
1393 passed in as a single string.
1394 """
1395 if ((self.encoding is not None) and
1396 (self.encoding.lower() not in BOM_LIST)):
1397
1398
1399
1400 return self._decode(infile, self.encoding)
1401
1402 if isinstance(infile, (list, tuple)):
1403 line = infile[0]
1404 else:
1405 line = infile
1406 if self.encoding is not None:
1407
1408
1409
1410
1411 enc = BOM_LIST[self.encoding.lower()]
1412 if enc == 'utf_16':
1413
1414 for BOM, (encoding, final_encoding) in BOMS.items():
1415 if not final_encoding:
1416
1417 continue
1418 if infile.startswith(BOM):
1419
1420
1421
1422 return self._decode(infile, encoding)
1423
1424
1425
1426 return self._decode(infile, self.encoding)
1427
1428
1429 BOM = BOM_SET[enc]
1430 if not line.startswith(BOM):
1431 return self._decode(infile, self.encoding)
1432
1433 newline = line[len(BOM):]
1434
1435
1436 if isinstance(infile, (list, tuple)):
1437 infile[0] = newline
1438 else:
1439 infile = newline
1440 self.BOM = True
1441 return self._decode(infile, self.encoding)
1442
1443
1444 for BOM, (encoding, final_encoding) in BOMS.items():
1445 if not line.startswith(BOM):
1446 continue
1447 else:
1448
1449 self.encoding = final_encoding
1450 if not final_encoding:
1451 self.BOM = True
1452
1453
1454 newline = line[len(BOM):]
1455 if isinstance(infile, (list, tuple)):
1456 infile[0] = newline
1457 else:
1458 infile = newline
1459
1460 if isinstance(infile, basestring):
1461 return infile.splitlines(True)
1462 else:
1463 return infile
1464
1465 return self._decode(infile, encoding)
1466
1467
1468 if isinstance(infile, basestring):
1469
1470 return infile.splitlines(True)
1471 return infile
1472
1473
1475 """Decode ASCII strings to unicode if a self.encoding is specified."""
1476 if self.encoding:
1477 return aString.decode('ascii')
1478 else:
1479 return aString
1480
1481
1482 - def _decode(self, infile, encoding):
1483 """
1484 Decode infile to unicode. Using the specified encoding.
1485
1486 if is a string, it also needs converting to a list.
1487 """
1488 if isinstance(infile, basestring):
1489
1490
1491 return infile.decode(encoding).splitlines(True)
1492 for i, line in enumerate(infile):
1493 if not isinstance(line, unicode):
1494
1495
1496
1497 infile[i] = line.decode(encoding)
1498 return infile
1499
1500
1502 """Decode element to unicode if necessary."""
1503 if not self.encoding:
1504 return line
1505 if isinstance(line, str) and self.default_encoding:
1506 return line.decode(self.default_encoding)
1507 return line
1508
1509
1510 - def _str(self, value):
1511 """
1512 Used by ``stringify`` within validate, to turn non-string values
1513 into strings.
1514 """
1515 if not isinstance(value, basestring):
1516 return str(value)
1517 else:
1518 return value
1519
1520
1522 """Actually parse the config file."""
1523 temp_list_values = self.list_values
1524 if self.unrepr:
1525 self.list_values = False
1526
1527 comment_list = []
1528 done_start = False
1529 this_section = self
1530 maxline = len(infile) - 1
1531 cur_index = -1
1532 reset_comment = False
1533
1534 while cur_index < maxline:
1535 if reset_comment:
1536 comment_list = []
1537 cur_index += 1
1538 line = infile[cur_index]
1539 sline = line.strip()
1540
1541 if not sline or sline.startswith('#'):
1542 reset_comment = False
1543 comment_list.append(line)
1544 continue
1545
1546 if not done_start:
1547
1548 self.initial_comment = comment_list
1549 comment_list = []
1550 done_start = True
1551
1552 reset_comment = True
1553
1554 mat = self._sectionmarker.match(line)
1555 if mat is not None:
1556
1557 (indent, sect_open, sect_name, sect_close, comment) = mat.groups()
1558 if indent and (self.indent_type is None):
1559 self.indent_type = indent
1560 cur_depth = sect_open.count('[')
1561 if cur_depth != sect_close.count(']'):
1562 self._handle_error("Cannot compute the section depth at line %s.",
1563 NestingError, infile, cur_index)
1564 continue
1565
1566 if cur_depth < this_section.depth:
1567
1568 try:
1569 parent = self._match_depth(this_section,
1570 cur_depth).parent
1571 except SyntaxError:
1572 self._handle_error("Cannot compute nesting level at line %s.",
1573 NestingError, infile, cur_index)
1574 continue
1575 elif cur_depth == this_section.depth:
1576
1577 parent = this_section.parent
1578 elif cur_depth == this_section.depth + 1:
1579
1580 parent = this_section
1581 else:
1582 self._handle_error("Section too nested at line %s.",
1583 NestingError, infile, cur_index)
1584
1585 sect_name = self._unquote(sect_name)
1586 if sect_name in parent:
1587 self._handle_error('Duplicate section name at line %s.',
1588 DuplicateError, infile, cur_index)
1589 continue
1590
1591
1592 this_section = Section(
1593 parent,
1594 cur_depth,
1595 self,
1596 name=sect_name)
1597 parent[sect_name] = this_section
1598 parent.inline_comments[sect_name] = comment
1599 parent.comments[sect_name] = comment_list
1600 continue
1601
1602
1603
1604 mat = self._keyword.match(line)
1605 if mat is None:
1606
1607
1608 self._handle_error(
1609 'Invalid line at line "%s".',
1610 ParseError, infile, cur_index)
1611 else:
1612
1613
1614 (indent, key, value) = mat.groups()
1615 if indent and (self.indent_type is None):
1616 self.indent_type = indent
1617
1618 if value[:3] in ['"""', "'''"]:
1619 try:
1620 value, comment, cur_index = self._multiline(
1621 value, infile, cur_index, maxline)
1622 except SyntaxError:
1623 self._handle_error(
1624 'Parse error in value at line %s.',
1625 ParseError, infile, cur_index)
1626 continue
1627 else:
1628 if self.unrepr:
1629 comment = ''
1630 try:
1631 value = unrepr(value)
1632 except Exception, e:
1633 if type(e) == UnknownType:
1634 msg = 'Unknown name or type in value at line %s.'
1635 else:
1636 msg = 'Parse error in value at line %s.'
1637 self._handle_error(msg, UnreprError, infile,
1638 cur_index)
1639 continue
1640 else:
1641 if self.unrepr:
1642 comment = ''
1643 try:
1644 value = unrepr(value)
1645 except Exception, e:
1646 if isinstance(e, UnknownType):
1647 msg = 'Unknown name or type in value at line %s.'
1648 else:
1649 msg = 'Parse error in value at line %s.'
1650 self._handle_error(msg, UnreprError, infile,
1651 cur_index)
1652 continue
1653 else:
1654
1655 try:
1656 (value, comment) = self._handle_value(value)
1657 except SyntaxError:
1658 self._handle_error(
1659 'Parse error in value at line %s.',
1660 ParseError, infile, cur_index)
1661 continue
1662
1663 key = self._unquote(key)
1664 if key in this_section:
1665 self._handle_error(
1666 'Duplicate keyword name at line %s.',
1667 DuplicateError, infile, cur_index)
1668 continue
1669
1670
1671
1672 this_section.__setitem__(key, value, unrepr=True)
1673 this_section.inline_comments[key] = comment
1674 this_section.comments[key] = comment_list
1675 continue
1676
1677 if self.indent_type is None:
1678
1679 self.indent_type = ''
1680
1681
1682 if not self and not self.initial_comment:
1683 self.initial_comment = comment_list
1684 elif not reset_comment:
1685 self.final_comment = comment_list
1686 self.list_values = temp_list_values
1687
1688
1690 """
1691 Given a section and a depth level, walk back through the sections
1692 parents to see if the depth level matches a previous section.
1693
1694 Return a reference to the right section,
1695 or raise a SyntaxError.
1696 """
1697 while depth < sect.depth:
1698 if sect is sect.parent:
1699
1700 raise SyntaxError()
1701 sect = sect.parent
1702 if sect.depth == depth:
1703 return sect
1704
1705 raise SyntaxError()
1706
1707
1709 """
1710 Handle an error according to the error settings.
1711
1712 Either raise the error or store it.
1713 The error will have occured at ``cur_index``
1714 """
1715 line = infile[cur_index]
1716 cur_index += 1
1717 message = text % cur_index
1718 error = ErrorClass(message, cur_index, line)
1719 if self.raise_errors:
1720
1721 raise error
1722
1723
1724 self._errors.append(error)
1725
1726
1728 """Return an unquoted version of a value"""
1729 if not value:
1730
1731 raise SyntaxError
1732 if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1733 value = value[1:-1]
1734 return value
1735
1736
1737 - def _quote(self, value, multiline=True):
1738 """
1739 Return a safely quoted version of a value.
1740
1741 Raise a ConfigObjError if the value cannot be safely quoted.
1742 If multiline is ``True`` (default) then use triple quotes
1743 if necessary.
1744
1745 * Don't quote values that don't need it.
1746 * Recursively quote members of a list and return a comma joined list.
1747 * Multiline is ``False`` for lists.
1748 * Obey list syntax for empty and single member lists.
1749
1750 If ``list_values=False`` then the value is only quoted if it contains
1751 a ``\\n`` (is multiline) or '#'.
1752
1753 If ``write_empty_values`` is set, and the value is an empty string, it
1754 won't be quoted.
1755 """
1756 if multiline and self.write_empty_values and value == '':
1757
1758
1759 return ''
1760
1761 if multiline and isinstance(value, (list, tuple)):
1762 if not value:
1763 return ','
1764 elif len(value) == 1:
1765 return self._quote(value[0], multiline=False) + ','
1766 return ', '.join([self._quote(val, multiline=False)
1767 for val in value])
1768 if not isinstance(value, basestring):
1769 if self.stringify:
1770 value = str(value)
1771 else:
1772 raise TypeError('Value "%s" is not a string.' % value)
1773
1774 if not value:
1775 return '""'
1776
1777 no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value
1778 need_triple = multiline and ((("'" in value) and ('"' in value)) or ('\n' in value ))
1779 hash_triple_quote = multiline and not need_triple and ("'" in value) and ('"' in value) and ('#' in value)
1780 check_for_single = (no_lists_no_quotes or not need_triple) and not hash_triple_quote
1781
1782 if check_for_single:
1783 if not self.list_values:
1784
1785 quot = noquot
1786
1787 elif '\n' in value:
1788
1789 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1790 elif ((value[0] not in wspace_plus) and
1791 (value[-1] not in wspace_plus) and
1792 (',' not in value)):
1793 quot = noquot
1794 else:
1795 quot = self._get_single_quote(value)
1796 else:
1797
1798 quot = self._get_triple_quote(value)
1799
1800 if quot == noquot and '#' in value and self.list_values:
1801 quot = self._get_single_quote(value)
1802
1803 return quot % value
1804
1805
1807 if ("'" in value) and ('"' in value):
1808 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1809 elif '"' in value:
1810 quot = squot
1811 else:
1812 quot = dquot
1813 return quot
1814
1815
1817 if (value.find('"""') != -1) and (value.find("'''") != -1):
1818 raise ConfigObjError('Value "%s" cannot be safely quoted.' % value)
1819 if value.find('"""') == -1:
1820 quot = tsquot
1821 else:
1822 quot = tdquot
1823 return quot
1824
1825
1827 """
1828 Given a value string, unquote, remove comment,
1829 handle lists. (including empty and single member lists)
1830 """
1831 if self._inspec:
1832
1833 return (value, '')
1834
1835 if not self.list_values:
1836 mat = self._nolistvalue.match(value)
1837 if mat is None:
1838 raise SyntaxError()
1839
1840 return mat.groups()
1841
1842 mat = self._valueexp.match(value)
1843 if mat is None:
1844
1845
1846 raise SyntaxError()
1847 (list_values, single, empty_list, comment) = mat.groups()
1848 if (list_values == '') and (single is None):
1849
1850 raise SyntaxError()
1851
1852
1853 if empty_list is not None:
1854
1855 return ([], comment)
1856 if single is not None:
1857
1858 if list_values and not single:
1859
1860
1861 single = None
1862 else:
1863 single = single or '""'
1864 single = self._unquote(single)
1865 if list_values == '':
1866
1867 return (single, comment)
1868 the_list = self._listvalueexp.findall(list_values)
1869 the_list = [self._unquote(val) for val in the_list]
1870 if single is not None:
1871 the_list += [single]
1872 return (the_list, comment)
1873
1874
1875 - def _multiline(self, value, infile, cur_index, maxline):
1876 """Extract the value, where we are in a multiline situation."""
1877 quot = value[:3]
1878 newvalue = value[3:]
1879 single_line = self._triple_quote[quot][0]
1880 multi_line = self._triple_quote[quot][1]
1881 mat = single_line.match(value)
1882 if mat is not None:
1883 retval = list(mat.groups())
1884 retval.append(cur_index)
1885 return retval
1886 elif newvalue.find(quot) != -1:
1887
1888 raise SyntaxError()
1889
1890 while cur_index < maxline:
1891 cur_index += 1
1892 newvalue += '\n'
1893 line = infile[cur_index]
1894 if line.find(quot) == -1:
1895 newvalue += line
1896 else:
1897
1898 break
1899 else:
1900
1901 raise SyntaxError()
1902 mat = multi_line.match(line)
1903 if mat is None:
1904
1905 raise SyntaxError()
1906 (value, comment) = mat.groups()
1907 return (newvalue + value, comment, cur_index)
1908
1909
1911 """Parse the configspec."""
1912
1913
1914 if not isinstance(configspec, ConfigObj):
1915 try:
1916 configspec = ConfigObj(configspec,
1917 raise_errors=True,
1918 file_error=True,
1919 _inspec=True)
1920 except ConfigObjError, e:
1921
1922
1923 raise ConfigspecError('Parsing configspec failed: %s' % e)
1924 except IOError, e:
1925 raise IOError('Reading configspec failed: %s' % e)
1926
1927 self.configspec = configspec
1928
1929
1930
1932 """
1933 Called by validate. Handles setting the configspec on subsections
1934 including sections to be validated by __many__
1935 """
1936 configspec = section.configspec
1937 many = configspec.get('__many__')
1938 if isinstance(many, dict):
1939 for entry in section.sections:
1940 if entry not in configspec:
1941 section[entry].configspec = many
1942
1943 for entry in configspec.sections:
1944 if entry == '__many__':
1945 continue
1946 if entry not in section:
1947 section[entry] = {}
1948 section[entry]._created = True
1949 if copy:
1950
1951 section.comments[entry] = configspec.comments.get(entry, [])
1952 section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
1953
1954
1955 if isinstance(section[entry], Section):
1956 section[entry].configspec = configspec[entry]
1957
1958
1959 - def _write_line(self, indent_string, entry, this_entry, comment):
1960 """Write an individual line, for the write method"""
1961
1962 if not self.unrepr:
1963 val = self._decode_element(self._quote(this_entry))
1964 else:
1965 val = repr(this_entry)
1966 return '%s%s%s%s%s' % (indent_string,
1967 self._decode_element(self._quote(entry, multiline=False)),
1968 self._a_to_u(' = '),
1969 val,
1970 self._decode_element(comment))
1971
1972
1980
1981
1990
1991
1992
1993
1994 - def write(self, outfile=None, section=None):
1995 """
1996 Write the current ConfigObj as a file
1997
1998 tekNico: FIXME: use StringIO instead of real files
1999
2000 >>> filename = a.filename
2001 >>> a.filename = 'test.ini'
2002 >>> a.write()
2003 >>> a.filename = filename
2004 >>> a == ConfigObj('test.ini', raise_errors=True)
2005 1
2006 >>> import os
2007 >>> os.remove('test.ini')
2008 """
2009 if self.indent_type is None:
2010
2011 self.indent_type = DEFAULT_INDENT_TYPE
2012
2013 out = []
2014 cs = self._a_to_u('#')
2015 csp = self._a_to_u('# ')
2016 if section is None:
2017 int_val = self.interpolation
2018 self.interpolation = False
2019 section = self
2020 for line in self.initial_comment:
2021 line = self._decode_element(line)
2022 stripped_line = line.strip()
2023 if stripped_line and not stripped_line.startswith(cs):
2024 line = csp + line
2025 out.append(line)
2026
2027 indent_string = self.indent_type * section.depth
2028 for entry in (section.scalars + section.sections):
2029 if entry in section.defaults:
2030
2031 continue
2032 for comment_line in section.comments[entry]:
2033 comment_line = self._decode_element(comment_line.lstrip())
2034 if comment_line and not comment_line.startswith(cs):
2035 comment_line = csp + comment_line
2036 out.append(indent_string + comment_line)
2037 this_entry = section[entry]
2038 comment = self._handle_comment(section.inline_comments[entry])
2039
2040 if isinstance(this_entry, dict):
2041
2042 out.append(self._write_marker(
2043 indent_string,
2044 this_entry.depth,
2045 entry,
2046 comment))
2047 out.extend(self.write(section=this_entry))
2048 else:
2049 out.append(self._write_line(
2050 indent_string,
2051 entry,
2052 this_entry,
2053 comment))
2054
2055 if section is self:
2056 for line in self.final_comment:
2057 line = self._decode_element(line)
2058 stripped_line = line.strip()
2059 if stripped_line and not stripped_line.startswith(cs):
2060 line = csp + line
2061 out.append(line)
2062 self.interpolation = int_val
2063
2064 if section is not self:
2065 return out
2066
2067 if (self.filename is None) and (outfile is None):
2068
2069
2070
2071 if self.encoding:
2072 out = [l.encode(self.encoding) for l in out]
2073 if (self.BOM and ((self.encoding is None) or
2074 (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
2075
2076 if not out:
2077 out.append('')
2078 out[0] = BOM_UTF8 + out[0]
2079 return out
2080
2081
2082 newline = self.newlines or os.linesep
2083 if (getattr(outfile, 'mode', None) is not None and outfile.mode == 'w'
2084 and sys.platform == 'win32' and newline == '\r\n'):
2085
2086 newline = '\n'
2087 output = self._a_to_u(newline).join(out)
2088 if self.encoding:
2089 output = output.encode(self.encoding)
2090 if self.BOM and ((self.encoding is None) or match_utf8(self.encoding)):
2091
2092 output = BOM_UTF8 + output
2093
2094 if not output.endswith(newline):
2095 output += newline
2096 if outfile is not None:
2097 outfile.write(output)
2098 else:
2099 h = open(self.filename, 'wb')
2100 h.write(output)
2101 h.close()
2102
2103
2104 - def validate(self, validator, preserve_errors=False, copy=False,
2105 section=None):
2106 """
2107 Test the ConfigObj against a configspec.
2108
2109 It uses the ``validator`` object from *validate.py*.
2110
2111 To run ``validate`` on the current ConfigObj, call: ::
2112
2113 test = config.validate(validator)
2114
2115 (Normally having previously passed in the configspec when the ConfigObj
2116 was created - you can dynamically assign a dictionary of checks to the
2117 ``configspec`` attribute of a section though).
2118
2119 It returns ``True`` if everything passes, or a dictionary of
2120 pass/fails (True/False). If every member of a subsection passes, it
2121 will just have the value ``True``. (It also returns ``False`` if all
2122 members fail).
2123
2124 In addition, it converts the values from strings to their native
2125 types if their checks pass (and ``stringify`` is set).
2126
2127 If ``preserve_errors`` is ``True`` (``False`` is default) then instead
2128 of a marking a fail with a ``False``, it will preserve the actual
2129 exception object. This can contain info about the reason for failure.
2130 For example the ``VdtValueTooSmallError`` indicates that the value
2131 supplied was too small. If a value (or section) is missing it will
2132 still be marked as ``False``.
2133
2134 You must have the validate module to use ``preserve_errors=True``.
2135
2136 You can then use the ``flatten_errors`` function to turn your nested
2137 results dictionary into a flattened list of failures - useful for
2138 displaying meaningful error messages.
2139 """
2140 if section is None:
2141 if self.configspec is None:
2142 raise ValueError('No configspec supplied.')
2143 if preserve_errors:
2144
2145
2146 from validate import VdtMissingValue
2147 self._vdtMissingValue = VdtMissingValue
2148
2149 section = self
2150
2151 if copy:
2152 section.initial_comment = section.configspec.initial_comment
2153 section.final_comment = section.configspec.final_comment
2154 section.encoding = section.configspec.encoding
2155 section.BOM = section.configspec.BOM
2156 section.newlines = section.configspec.newlines
2157 section.indent_type = section.configspec.indent_type
2158
2159
2160
2161 configspec = section.configspec
2162 self._set_configspec(section, copy)
2163
2164
2165 def validate_entry(entry, spec, val, missing, ret_true, ret_false):
2166 section.default_values.pop(entry, None)
2167
2168 try:
2169 section.default_values[entry] = validator.get_default_value(configspec[entry])
2170 except (KeyError, AttributeError, validator.baseErrorClass):
2171
2172
2173 pass
2174
2175 try:
2176 check = validator.check(spec,
2177 val,
2178 missing=missing
2179 )
2180 except validator.baseErrorClass, e:
2181 if not preserve_errors or isinstance(e, self._vdtMissingValue):
2182 out[entry] = False
2183 else:
2184
2185 out[entry] = e
2186 ret_false = False
2187 ret_true = False
2188 else:
2189 ret_false = False
2190 out[entry] = True
2191 if self.stringify or missing:
2192
2193
2194 if not self.stringify:
2195 if isinstance(check, (list, tuple)):
2196
2197 check = [self._str(item) for item in check]
2198 elif missing and check is None:
2199
2200 check = ''
2201 else:
2202 check = self._str(check)
2203 if (check != val) or missing:
2204 section[entry] = check
2205 if not copy and missing and entry not in section.defaults:
2206 section.defaults.append(entry)
2207 return ret_true, ret_false
2208
2209
2210 out = {}
2211 ret_true = True
2212 ret_false = True
2213
2214 unvalidated = [k for k in section.scalars if k not in configspec]
2215 incorrect_sections = [k for k in configspec.sections if k in section.scalars]
2216 incorrect_scalars = [k for k in configspec.scalars if k in section.sections]
2217
2218 for entry in configspec.scalars:
2219 if entry in ('__many__', '___many___'):
2220
2221 continue
2222 if (not entry in section.scalars) or (entry in section.defaults):
2223
2224
2225 missing = True
2226 val = None
2227 if copy and entry not in section.scalars:
2228
2229 section.comments[entry] = (
2230 configspec.comments.get(entry, []))
2231 section.inline_comments[entry] = (
2232 configspec.inline_comments.get(entry, ''))
2233
2234 else:
2235 missing = False
2236 val = section[entry]
2237
2238 ret_true, ret_false = validate_entry(entry, configspec[entry], val,
2239 missing, ret_true, ret_false)
2240
2241 many = None
2242 if '__many__' in configspec.scalars:
2243 many = configspec['__many__']
2244 elif '___many___' in configspec.scalars:
2245 many = configspec['___many___']
2246
2247 if many is not None:
2248 for entry in unvalidated:
2249 val = section[entry]
2250 ret_true, ret_false = validate_entry(entry, many, val, False,
2251 ret_true, ret_false)
2252 unvalidated = []
2253
2254 for entry in incorrect_scalars:
2255 ret_true = False
2256 if not preserve_errors:
2257 out[entry] = False
2258 else:
2259 ret_false = False
2260 msg = 'Value %r was provided as a section' % entry
2261 out[entry] = validator.baseErrorClass(msg)
2262 for entry in incorrect_sections:
2263 ret_true = False
2264 if not preserve_errors:
2265 out[entry] = False
2266 else:
2267 ret_false = False
2268 msg = 'Section %r was provided as a single value' % entry
2269 out[entry] = validator.baseErrorClass(msg)
2270
2271
2272
2273 for entry in section.sections:
2274
2275 if section is self and entry == 'DEFAULT':
2276 continue
2277 if section[entry].configspec is None:
2278 unvalidated.append(entry)
2279 continue
2280 if copy:
2281 section.comments[entry] = configspec.comments.get(entry, [])
2282 section.inline_comments[entry] = configspec.inline_comments.get(entry, '')
2283 check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=section[entry])
2284 out[entry] = check
2285 if check == False:
2286 ret_true = False
2287 elif check == True:
2288 ret_false = False
2289 else:
2290 ret_true = False
2291
2292 section.extra_values = unvalidated
2293 if preserve_errors and not section._created:
2294
2295
2296 ret_false = False
2297
2298 if ret_false and preserve_errors and out:
2299
2300
2301
2302
2303 ret_false = not any(out.values())
2304 if ret_true:
2305 return True
2306 elif ret_false:
2307 return False
2308 return out
2309
2310
2312 """Clear ConfigObj instance and restore to 'freshly created' state."""
2313 self.clear()
2314 self._initialise()
2315
2316
2317 self.configspec = None
2318
2319 self._original_configspec = None
2320
2321
2323 """
2324 Reload a ConfigObj from file.
2325
2326 This method raises a ``ReloadError`` if the ConfigObj doesn't have
2327 a filename attribute pointing to a file.
2328 """
2329 if not isinstance(self.filename, basestring):
2330 raise ReloadError()
2331
2332 filename = self.filename
2333 current_options = {}
2334 for entry in OPTION_DEFAULTS:
2335 if entry == 'configspec':
2336 continue
2337 current_options[entry] = getattr(self, entry)
2338
2339 configspec = self._original_configspec
2340 current_options['configspec'] = configspec
2341
2342 self.clear()
2343 self._initialise(current_options)
2344 self._load(filename, configspec)
2345
2346
2347
2349 """
2350 A simple validator.
2351 Can be used to check that all members expected are present.
2352
2353 To use it, provide a configspec with all your members in (the value given
2354 will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
2355 method of your ``ConfigObj``. ``validate`` will return ``True`` if all
2356 members are present, or a dictionary with True/False meaning
2357 present/missing. (Whole missing sections will be replaced with ``False``)
2358 """
2359
2362
2363 - def check(self, check, member, missing=False):
2364 """A dummy check method, always returns the value unchanged."""
2365 if missing:
2366 raise self.baseErrorClass()
2367 return member
2368
2369
2371 """
2372 An example function that will turn a nested dictionary of results
2373 (as returned by ``ConfigObj.validate``) into a flat list.
2374
2375 ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
2376 dictionary returned by ``validate``.
2377
2378 (This is a recursive function, so you shouldn't use the ``levels`` or
2379 ``results`` arguments - they are used by the function.)
2380
2381 Returns a list of keys that failed. Each member of the list is a tuple::
2382
2383 ([list of sections...], key, result)
2384
2385 If ``validate`` was called with ``preserve_errors=False`` (the default)
2386 then ``result`` will always be ``False``.
2387
2388 *list of sections* is a flattened list of sections that the key was found
2389 in.
2390
2391 If the section was missing (or a section was expected and a scalar provided
2392 - or vice-versa) then key will be ``None``.
2393
2394 If the value (or section) was missing then ``result`` will be ``False``.
2395
2396 If ``validate`` was called with ``preserve_errors=True`` and a value
2397 was present, but failed the check, then ``result`` will be the exception
2398 object returned. You can use this as a string that describes the failure.
2399
2400 For example *The value "3" is of the wrong type*.
2401 """
2402 if levels is None:
2403
2404 levels = []
2405 results = []
2406 if res == True:
2407 return results
2408 if res == False or isinstance(res, Exception):
2409 results.append((levels[:], None, res))
2410 if levels:
2411 levels.pop()
2412 return results
2413 for (key, val) in res.items():
2414 if val == True:
2415 continue
2416 if isinstance(cfg.get(key), dict):
2417
2418 levels.append(key)
2419 flatten_errors(cfg[key], val, levels, results)
2420 continue
2421 results.append((levels[:], key, val))
2422
2423
2424 if levels:
2425 levels.pop()
2426
2427 return results
2428
2429
2431 """
2432 Find all the values and sections not in the configspec from a validated
2433 ConfigObj.
2434
2435 ``get_extra_values`` returns a list of tuples where each tuple represents
2436 either an extra section, or an extra value.
2437
2438 The tuples contain two values, a tuple representing the section the value
2439 is in and the name of the extra values. For extra values in the top level
2440 section the first member will be an empty tuple. For values in the 'foo'
2441 section the first member will be ``('foo',)``. For members in the 'bar'
2442 subsection of the 'foo' section the first member will be ``('foo', 'bar')``.
2443
2444 NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't
2445 been validated it will return an empty list.
2446 """
2447 out = []
2448
2449 out.extend([(_prepend, name) for name in conf.extra_values])
2450 for name in conf.sections:
2451 if name not in conf.extra_values:
2452 out.extend(get_extra_values(conf[name], _prepend + (name,)))
2453 return out
2454
2455
2456 """*A programming language is a medium of expression.* - Paul Graham"""
2457