Package pythonutils ::
Module configobj
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 from __future__ import generators
20
21 import sys
22 INTP_VER = sys.version_info[:2]
23 if INTP_VER < (2, 2):
24 raise RuntimeError("Python v.2.2 or later needed")
25
26 import os, re
27 import compiler
28 from types import StringTypes
29 from warnings import warn
30 from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE
31
32
33
34
35 BOMS = {
36 BOM_UTF8: ('utf_8', None),
37 BOM_UTF16_BE: ('utf16_be', 'utf_16'),
38 BOM_UTF16_LE: ('utf16_le', 'utf_16'),
39 BOM_UTF16: ('utf_16', 'utf_16'),
40 }
41
42
43
44 BOM_LIST = {
45 'utf_16': 'utf_16',
46 'u16': 'utf_16',
47 'utf16': 'utf_16',
48 'utf-16': 'utf_16',
49 'utf16_be': 'utf16_be',
50 'utf_16_be': 'utf16_be',
51 'utf-16be': 'utf16_be',
52 'utf16_le': 'utf16_le',
53 'utf_16_le': 'utf16_le',
54 'utf-16le': 'utf16_le',
55 'utf_8': 'utf_8',
56 'u8': 'utf_8',
57 'utf': 'utf_8',
58 'utf8': 'utf_8',
59 'utf-8': 'utf_8',
60 }
61
62
63 BOM_SET = {
64 'utf_8': BOM_UTF8,
65 'utf_16': BOM_UTF16,
66 'utf16_be': BOM_UTF16_BE,
67 'utf16_le': BOM_UTF16_LE,
68 None: BOM_UTF8
69 }
70
71 try:
72 from validate import VdtMissingValue
73 except ImportError:
74 VdtMissingValue = None
75
76 try:
77 enumerate
78 except NameError:
79 def enumerate(obj):
80 """enumerate for Python 2.2."""
81 i = -1
82 for item in obj:
83 i += 1
84 yield i, item
85
86 try:
87 True, False
88 except NameError:
89 True, False = 1, 0
90
91
92 __version__ = '4.3.1'
93
94 __revision__ = '$Id: configobj.py 156 2006-01-31 14:57:08Z fuzzyman $'
95
96 __docformat__ = "restructuredtext en"
97
98
99
100
101
102 __all__ = (
103 '__version__',
104 'DEFAULT_INDENT_TYPE',
105 'NUM_INDENT_SPACES',
106 'MAX_INTERPOL_DEPTH',
107 'ConfigObjError',
108 'NestingError',
109 'ParseError',
110 'DuplicateError',
111 'ConfigspecError',
112 'ConfigObj',
113 'SimpleVal',
114 'InterpolationError',
115 'InterpolationDepthError',
116 'MissingInterpolationOption',
117 'RepeatSectionError',
118 'UnknownType',
119 '__docformat__',
120 'flatten_errors',
121 )
122
123 DEFAULT_INDENT_TYPE = ' '
124 NUM_INDENT_SPACES = 4
125 MAX_INTERPOL_DEPTH = 10
126
127 OPTION_DEFAULTS = {
128 'interpolation': True,
129 'raise_errors': False,
130 'list_values': True,
131 'create_empty': False,
132 'file_error': False,
133 'configspec': None,
134 'stringify': True,
135
136 'indent_type': None,
137 'encoding': None,
138 'default_encoding': None,
139 'unrepr': False,
140 'write_empty_values': False,
141 }
142
143
145 s = "a=" + s
146 p = compiler.parse(s)
147 return p.getChildren()[1].getChildren()[0].getChildren()[1]
148
150 pass
151
153
155 m = getattr(self, 'build_' + o.__class__.__name__, None)
156 if m is None:
157 raise UnknownType(o.__class__.__name__)
158 return m(o)
159
161 return map(self.build, o.getChildren())
162
164 return o.value
165
167 d = {}
168 i = iter(map(self.build, o.getChildren()))
169 for el in i:
170 d[el] = i.next()
171 return d
172
175
177 if o.name == 'None':
178 return None
179 if o.name == 'True':
180 return True
181 if o.name == 'False':
182 return False
183
184
185 raise UnknownType('Undefined Name')
186
188 real, imag = map(self.build_Const, o.getChildren())
189 try:
190 real = float(real)
191 except TypeError:
192 raise UnknownType('Add')
193 if not isinstance(imag, complex) or imag.real != 0.0:
194 raise UnknownType('Add')
195 return real+imag
196
198 parent = self.build(o.expr)
199 return getattr(parent, o.attrname)
200
203
206
211
212
214 """
215 This is the base class for all errors that ConfigObj raises.
216 It is a subclass of SyntaxError.
217 """
218 - def __init__(self, message='', line_number=None, line=''):
219 self.line = line
220 self.line_number = line_number
221 self.message = message
222 SyntaxError.__init__(self, message)
223
225 """
226 This error indicates a level of nesting that doesn't match.
227 """
228
230 """
231 This error indicates that a line is badly written.
232 It is neither a valid ``key = value`` line,
233 nor a valid section marker line.
234 """
235
237 """
238 The keyword or section specified already exists.
239 """
240
242 """
243 An error occured whilst parsing a configspec.
244 """
245
247 """Base class for the two interpolation errors."""
248
250 """Maximum interpolation depth exceeded in string interpolation."""
251
256
258 """
259 This error indicates additional sections in a section with a
260 ``__many__`` (repeated) section.
261 """
262
264 """A value specified for interpolation was missing."""
265
270
272 """
273 A dictionary-like object that represents a section in a config file.
274
275 It does string interpolation if the 'interpolate' attribute
276 of the 'main' object is set to True.
277
278 Interpolation is tried first from the 'DEFAULT' section of this object,
279 next from the 'DEFAULT' section of the parent, lastly the main object.
280
281 A Section will behave like an ordered dictionary - following the
282 order of the ``scalars`` and ``sections`` attributes.
283 You can use this to change the order of members.
284
285 Iteration follows the order: scalars, then sections.
286 """
287
288 _KEYCRE = re.compile(r"%\(([^)]*)\)s|.")
289
290 - def __init__(self, parent, depth, main, indict=None, name=None):
291 """
292 * parent is the section above
293 * depth is the depth level of this section
294 * main is the main ConfigObj
295 * indict is a dictionary to initialise the section with
296 """
297 if indict is None:
298 indict = {}
299 dict.__init__(self)
300
301 self.parent = parent
302
303 self.main = main
304
305 self.depth = depth
306
307 self.scalars = []
308
309 self.sections = []
310
311 self.name = name
312
313 self.comments = {}
314 self.inline_comments = {}
315
316 self.configspec = {}
317 self._order = []
318 self._configspec_comments = {}
319 self._configspec_inline_comments = {}
320 self._cs_section_comments = {}
321 self._cs_section_inline_comments = {}
322
323 self.defaults = []
324
325
326
327 for entry in indict:
328 self[entry] = indict[entry]
329
343
345 """ """
346 s = match.group(1)
347 if s is None:
348 return match.group()
349 else:
350
351 self.main.interpolation = False
352
353 val = self.get('DEFAULT', {}).get(s)
354
355 if val is None:
356 val = self.parent.get('DEFAULT', {}).get(s)
357
358 if val is None:
359 val = self.main.get('DEFAULT', {}).get(s)
360 self.main.interpolation = True
361 if val is None:
362 raise MissingInterpolationOption(s)
363 return val
364
366 """Fetch the item and do string interpolation."""
367 val = dict.__getitem__(self, key)
368 if self.main.interpolation and isinstance(val, StringTypes):
369 return self._interpolate(val)
370 return val
371
373 """
374 Correctly set a value.
375
376 Making dictionary values Section instances.
377 (We have to special case 'Section' instances - which are also dicts)
378
379 Keys must be strings.
380 Values need only be strings (or lists of strings) if
381 ``main.stringify`` is set.
382
383 `unrepr`` must be set when setting a value to a dictionary, without
384 creating a new sub-section.
385 """
386 if not isinstance(key, StringTypes):
387 raise ValueError, 'The key "%s" is not a string.' % key
388
389 if not self.comments.has_key(key):
390 self.comments[key] = []
391 self.inline_comments[key] = ''
392
393 if key in self.defaults:
394 self.defaults.remove(key)
395
396 if isinstance(value, Section):
397 if not self.has_key(key):
398 self.sections.append(key)
399 dict.__setitem__(self, key, value)
400 elif isinstance(value, dict)and not unrepr:
401
402
403 if not self.has_key(key):
404 self.sections.append(key)
405 new_depth = self.depth + 1
406 dict.__setitem__(
407 self,
408 key,
409 Section(
410 self,
411 new_depth,
412 self.main,
413 indict=value,
414 name=key))
415 else:
416 if not self.has_key(key):
417 self.scalars.append(key)
418 if not self.main.stringify:
419 if isinstance(value, StringTypes):
420 pass
421 elif isinstance(value, (list, tuple)):
422 for entry in value:
423 if not isinstance(entry, StringTypes):
424 raise TypeError, (
425 'Value is not a string "%s".' % entry)
426 else:
427 raise TypeError, 'Value is not a string "%s".' % value
428 dict.__setitem__(self, key, value)
429
431 """Remove items from the sequence when deleting."""
432 dict. __delitem__(self, key)
433 if key in self.scalars:
434 self.scalars.remove(key)
435 else:
436 self.sections.remove(key)
437 del self.comments[key]
438 del self.inline_comments[key]
439
440 - def get(self, key, default=None):
441 """A version of ``get`` that doesn't bypass string interpolation."""
442 try:
443 return self[key]
444 except KeyError:
445 return default
446
448 """
449 A version of update that uses our ``__setitem__``.
450 """
451 for entry in indict:
452 self[entry] = indict[entry]
453
454 - def pop(self, key, *args):
455 """ """
456 val = dict.pop(self, key, *args)
457 if key in self.scalars:
458 del self.comments[key]
459 del self.inline_comments[key]
460 self.scalars.remove(key)
461 elif key in self.sections:
462 del self.comments[key]
463 del self.inline_comments[key]
464 self.sections.remove(key)
465 if self.main.interpolation and isinstance(val, StringTypes):
466 return self._interpolate(val)
467 return val
468
470 """Pops the first (key,val)"""
471 sequence = (self.scalars + self.sections)
472 if not sequence:
473 raise KeyError, ": 'popitem(): dictionary is empty'"
474 key = sequence[0]
475 val = self[key]
476 del self[key]
477 return key, val
478
480 """
481 A version of clear that also affects scalars/sections
482 Also clears comments and configspec.
483
484 Leaves other attributes alone :
485 depth/main/parent are not affected
486 """
487 dict.clear(self)
488 self.scalars = []
489 self.sections = []
490 self.comments = {}
491 self.inline_comments = {}
492 self.configspec = {}
493
495 """A version of setdefault that sets sequence if appropriate."""
496 try:
497 return self[key]
498 except KeyError:
499 self[key] = default
500 return self[key]
501
503 """ """
504 return zip((self.scalars + self.sections), self.values())
505
507 """ """
508 return (self.scalars + self.sections)
509
511 """ """
512 return [self[key] for key in (self.scalars + self.sections)]
513
515 """ """
516 return iter(self.items())
517
519 """ """
520 return iter((self.scalars + self.sections))
521
522 __iter__ = iterkeys
523
525 """ """
526 return iter(self.values())
527
529 return '{%s}' % ', '.join([('%s: %s' % (repr(key), repr(self[key])))
530 for key in (self.scalars + self.sections)])
531
532 __str__ = __repr__
533
534
535
537 """
538 Return a deepcopy of self as a dictionary.
539
540 All members that are ``Section`` instances are recursively turned to
541 ordinary dictionaries - by calling their ``dict`` method.
542
543 >>> n = a.dict()
544 >>> n == a
545 1
546 >>> n is a
547 0
548 """
549 newdict = {}
550 for entry in self:
551 this_entry = self[entry]
552 if isinstance(this_entry, Section):
553 this_entry = this_entry.dict()
554 elif isinstance(this_entry, list):
555
556 this_entry = list(this_entry)
557 elif isinstance(this_entry, tuple):
558
559 this_entry = tuple(this_entry)
560 newdict[entry] = this_entry
561 return newdict
562
563 - def merge(self, indict):
564 """
565 A recursive update - useful for merging config files.
566
567 >>> a = '''[section1]
568 ... option1 = True
569 ... [[subsection]]
570 ... more_options = False
571 ... # end of file'''.splitlines()
572 >>> b = '''# File is user.ini
573 ... [section1]
574 ... option1 = False
575 ... # end of file'''.splitlines()
576 >>> c1 = ConfigObj(b)
577 >>> c2 = ConfigObj(a)
578 >>> c2.merge(c1)
579 >>> c2
580 {'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}
581 """
582 for key, val in indict.items():
583 if (key in self and isinstance(self[key], dict) and
584 isinstance(val, dict)):
585 self[key].merge(val)
586 else:
587 self[key] = val
588
589 - def rename(self, oldkey, newkey):
590 """
591 Change a keyname to another, without changing position in sequence.
592
593 Implemented so that transformations can be made on keys,
594 as well as on values. (used by encode and decode)
595
596 Also renames comments.
597 """
598 if oldkey in self.scalars:
599 the_list = self.scalars
600 elif oldkey in self.sections:
601 the_list = self.sections
602 else:
603 raise KeyError, 'Key "%s" not found.' % oldkey
604 pos = the_list.index(oldkey)
605
606 val = self[oldkey]
607 dict.__delitem__(self, oldkey)
608 dict.__setitem__(self, newkey, val)
609 the_list.remove(oldkey)
610 the_list.insert(pos, newkey)
611 comm = self.comments[oldkey]
612 inline_comment = self.inline_comments[oldkey]
613 del self.comments[oldkey]
614 del self.inline_comments[oldkey]
615 self.comments[newkey] = comm
616 self.inline_comments[newkey] = inline_comment
617
618 - def walk(self, function, raise_errors=True,
619 call_on_sections=False, **keywargs):
620 """
621 Walk every member and call a function on the keyword and value.
622
623 Return a dictionary of the return values
624
625 If the function raises an exception, raise the errror
626 unless ``raise_errors=False``, in which case set the return value to
627 ``False``.
628
629 Any unrecognised keyword arguments you pass to walk, will be pased on
630 to the function you pass in.
631
632 Note: if ``call_on_sections`` is ``True`` then - on encountering a
633 subsection, *first* the function is called for the *whole* subsection,
634 and then recurses into it's members. This means your function must be
635 able to handle strings, dictionaries and lists. This allows you
636 to change the key of subsections as well as for ordinary members. The
637 return value when called on the whole subsection has to be discarded.
638
639 See the encode and decode methods for examples, including functions.
640
641 .. caution::
642
643 You can use ``walk`` to transform the names of members of a section
644 but you mustn't add or delete members.
645
646 >>> config = '''[XXXXsection]
647 ... XXXXkey = XXXXvalue'''.splitlines()
648 >>> cfg = ConfigObj(config)
649 >>> cfg
650 {'XXXXsection': {'XXXXkey': 'XXXXvalue'}}
651 >>> def transform(section, key):
652 ... val = section[key]
653 ... newkey = key.replace('XXXX', 'CLIENT1')
654 ... section.rename(key, newkey)
655 ... if isinstance(val, (tuple, list, dict)):
656 ... pass
657 ... else:
658 ... val = val.replace('XXXX', 'CLIENT1')
659 ... section[newkey] = val
660 >>> cfg.walk(transform, call_on_sections=True)
661 {'CLIENT1section': {'CLIENT1key': None}}
662 >>> cfg
663 {'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}
664 """
665 out = {}
666
667 for i in range(len(self.scalars)):
668 entry = self.scalars[i]
669 try:
670 val = function(self, entry, **keywargs)
671
672 entry = self.scalars[i]
673 out[entry] = val
674 except Exception:
675 if raise_errors:
676 raise
677 else:
678 entry = self.scalars[i]
679 out[entry] = False
680
681 for i in range(len(self.sections)):
682 entry = self.sections[i]
683 if call_on_sections:
684 try:
685 function(self, entry, **keywargs)
686 except Exception:
687 if raise_errors:
688 raise
689 else:
690 entry = self.sections[i]
691 out[entry] = False
692
693 entry = self.sections[i]
694
695 out[entry] = self[entry].walk(
696 function,
697 raise_errors=raise_errors,
698 call_on_sections=call_on_sections,
699 **keywargs)
700 return out
701
703 """
704 Decode all strings and values to unicode, using the specified encoding.
705
706 Works with subsections and list values.
707
708 Uses the ``walk`` method.
709
710 Testing ``encode`` and ``decode``.
711 >>> m = ConfigObj(a)
712 >>> m.decode('ascii')
713 >>> def testuni(val):
714 ... for entry in val:
715 ... if not isinstance(entry, unicode):
716 ... print >> sys.stderr, type(entry)
717 ... raise AssertionError, 'decode failed.'
718 ... if isinstance(val[entry], dict):
719 ... testuni(val[entry])
720 ... elif not isinstance(val[entry], unicode):
721 ... raise AssertionError, 'decode failed.'
722 >>> testuni(m)
723 >>> m.encode('ascii')
724 >>> a == m
725 1
726 """
727 warn('use of ``decode`` is deprecated.', DeprecationWarning)
728 - def decode(section, key, encoding=encoding, warn=True):
729 """ """
730 val = section[key]
731 if isinstance(val, (list, tuple)):
732 newval = []
733 for entry in val:
734 newval.append(entry.decode(encoding))
735 elif isinstance(val, dict):
736 newval = val
737 else:
738 newval = val.decode(encoding)
739 newkey = key.decode(encoding)
740 section.rename(key, newkey)
741 section[newkey] = newval
742
743 self.walk(decode, call_on_sections=True)
744
746 """
747 Encode all strings and values from unicode,
748 using the specified encoding.
749
750 Works with subsections and list values.
751 Uses the ``walk`` method.
752 """
753 warn('use of ``encode`` is deprecated.', DeprecationWarning)
754 - def encode(section, key, encoding=encoding):
755 """ """
756 val = section[key]
757 if isinstance(val, (list, tuple)):
758 newval = []
759 for entry in val:
760 newval.append(entry.encode(encoding))
761 elif isinstance(val, dict):
762 newval = val
763 else:
764 newval = val.encode(encoding)
765 newkey = key.encode(encoding)
766 section.rename(key, newkey)
767 section[newkey] = newval
768 self.walk(encode, call_on_sections=True)
769
771 """A deprecated version of ``as_bool``."""
772 warn('use of ``istrue`` is deprecated. Use ``as_bool`` method '
773 'instead.', DeprecationWarning)
774 return self.as_bool(key)
775
777 """
778 Accepts a key as input. The corresponding value must be a string or
779 the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to
780 retain compatibility with Python 2.2.
781
782 If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns
783 ``True``.
784
785 If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns
786 ``False``.
787
788 ``as_bool`` is not case sensitive.
789
790 Any other input will raise a ``ValueError``.
791
792 >>> a = ConfigObj()
793 >>> a['a'] = 'fish'
794 >>> a.as_bool('a')
795 Traceback (most recent call last):
796 ValueError: Value "fish" is neither True nor False
797 >>> a['b'] = 'True'
798 >>> a.as_bool('b')
799 1
800 >>> a['b'] = 'off'
801 >>> a.as_bool('b')
802 0
803 """
804 val = self[key]
805 if val == True:
806 return True
807 elif val == False:
808 return False
809 else:
810 try:
811 if not isinstance(val, StringTypes):
812 raise KeyError
813 else:
814 return self.main._bools[val.lower()]
815 except KeyError:
816 raise ValueError('Value "%s" is neither True nor False' % val)
817
819 """
820 A convenience method which coerces the specified value to an integer.
821
822 If the value is an invalid literal for ``int``, a ``ValueError`` will
823 be raised.
824
825 >>> a = ConfigObj()
826 >>> a['a'] = 'fish'
827 >>> a.as_int('a')
828 Traceback (most recent call last):
829 ValueError: invalid literal for int(): fish
830 >>> a['b'] = '1'
831 >>> a.as_int('b')
832 1
833 >>> a['b'] = '3.2'
834 >>> a.as_int('b')
835 Traceback (most recent call last):
836 ValueError: invalid literal for int(): 3.2
837 """
838 return int(self[key])
839
841 """
842 A convenience method which coerces the specified value to a float.
843
844 If the value is an invalid literal for ``float``, a ``ValueError`` will
845 be raised.
846
847 >>> a = ConfigObj()
848 >>> a['a'] = 'fish'
849 >>> a.as_float('a')
850 Traceback (most recent call last):
851 ValueError: invalid literal for float(): fish
852 >>> a['b'] = '1'
853 >>> a.as_float('b')
854 1.0
855 >>> a['b'] = '3.2'
856 >>> a.as_float('b')
857 3.2000000000000002
858 """
859 return float(self[key])
860
861
863 """An object to read, create, and write config files."""
864
865 _keyword = re.compile(r'''^ # line start
866 (\s*) # indentation
867 ( # keyword
868 (?:".*?")| # double quotes
869 (?:'.*?')| # single quotes
870 (?:[^'"=].*?) # no quotes
871 )
872 \s*=\s* # divider
873 (.*) # value (including list values and comments)
874 $ # line end
875 ''',
876 re.VERBOSE)
877
878 _sectionmarker = re.compile(r'''^
879 (\s*) # 1: indentation
880 ((?:\[\s*)+) # 2: section marker open
881 ( # 3: section name open
882 (?:"\s*\S.*?\s*")| # at least one non-space with double quotes
883 (?:'\s*\S.*?\s*')| # at least one non-space with single quotes
884 (?:[^'"\s].*?) # at least one non-space unquoted
885 ) # section name close
886 ((?:\s*\])+) # 4: section marker close
887 \s*(\#.*)? # 5: optional comment
888 $''',
889 re.VERBOSE)
890
891
892
893
894
895 _valueexp = re.compile(r'''^
896 (?:
897 (?:
898 (
899 (?:
900 (?:
901 (?:".*?")| # double quotes
902 (?:'.*?')| # single quotes
903 (?:[^'",\#][^,\#]*?) # unquoted
904 )
905 \s*,\s* # comma
906 )* # match all list items ending in a comma (if any)
907 )
908 (
909 (?:".*?")| # double quotes
910 (?:'.*?')| # single quotes
911 (?:[^'",\#\s][^,]*?)| # unquoted
912 (?:(?<!,)) # Empty value
913 )? # last item in a list - or string value
914 )|
915 (,) # alternatively a single comma - empty list
916 )
917 \s*(\#.*)? # optional comment
918 $''',
919 re.VERBOSE)
920
921
922 _listvalueexp = re.compile(r'''
923 (
924 (?:".*?")| # double quotes
925 (?:'.*?')| # single quotes
926 (?:[^'",\#].*?) # unquoted
927 )
928 \s*,\s* # comma
929 ''',
930 re.VERBOSE)
931
932
933
934 _nolistvalue = re.compile(r'''^
935 (
936 (?:".*?")| # double quotes
937 (?:'.*?')| # single quotes
938 (?:[^'"\#].*?)| # unquoted
939 (?:) # Empty value
940 )
941 \s*(\#.*)? # optional comment
942 $''',
943 re.VERBOSE)
944
945
946 _single_line_single = re.compile(r"^'''(.*?)'''\s*(#.*)?$")
947 _single_line_double = re.compile(r'^"""(.*?)"""\s*(#.*)?$')
948 _multi_line_single = re.compile(r"^(.*?)'''\s*(#.*)?$")
949 _multi_line_double = re.compile(r'^(.*?)"""\s*(#.*)?$')
950
951 _triple_quote = {
952 "'''": (_single_line_single, _multi_line_single),
953 '"""': (_single_line_double, _multi_line_double),
954 }
955
956
957 _bools = {
958 'yes': True, 'no': False,
959 'on': True, 'off': False,
960 '1': True, '0': False,
961 'true': True, 'false': False,
962 }
963
964 - def __init__(self, infile=None, options=None, **kwargs):
965 """
966 Parse or create a config file object.
967
968 ``ConfigObj(infile=None, options=None, **kwargs)``
969 """
970 if infile is None:
971 infile = []
972 if options is None:
973 options = {}
974 else:
975 options = dict(options)
976
977 options.update(kwargs)
978
979 Section.__init__(self, self, 0, self)
980
981 defaults = OPTION_DEFAULTS.copy()
982 for entry in options.keys():
983 if entry not in defaults.keys():
984 raise TypeError, 'Unrecognised option "%s".' % entry
985
986
987
988 defaults.update(options)
989
990
991 self.filename = None
992 self._errors = []
993 self.raise_errors = defaults['raise_errors']
994 self.interpolation = defaults['interpolation']
995 self.list_values = defaults['list_values']
996 self.create_empty = defaults['create_empty']
997 self.file_error = defaults['file_error']
998 self.stringify = defaults['stringify']
999 self.indent_type = defaults['indent_type']
1000 self.encoding = defaults['encoding']
1001 self.default_encoding = defaults['default_encoding']
1002 self.BOM = False
1003 self.newlines = None
1004 self.write_empty_values = defaults['write_empty_values']
1005 self.unrepr = defaults['unrepr']
1006
1007 self.initial_comment = []
1008 self.final_comment = []
1009
1010 if isinstance(infile, StringTypes):
1011 self.filename = infile
1012 if os.path.isfile(infile):
1013 infile = open(infile).read() or []
1014 elif self.file_error:
1015
1016 raise IOError, 'Config file not found: "%s".' % self.filename
1017 else:
1018
1019 if self.create_empty:
1020
1021
1022 h = open(infile, 'w')
1023 h.write('')
1024 h.close()
1025 infile = []
1026 elif isinstance(infile, (list, tuple)):
1027 infile = list(infile)
1028 elif isinstance(infile, dict):
1029
1030
1031 if isinstance(infile, ConfigObj):
1032
1033 infile = infile.dict()
1034 for entry in infile:
1035 self[entry] = infile[entry]
1036 del self._errors
1037 if defaults['configspec'] is not None:
1038 self._handle_configspec(defaults['configspec'])
1039 else:
1040 self.configspec = None
1041 return
1042 elif hasattr(infile, 'read'):
1043
1044 infile = infile.read() or []
1045
1046
1047 else:
1048 raise TypeError, ('infile must be a filename,'
1049 ' file like object, or list of lines.')
1050
1051 if infile:
1052
1053 infile = self._handle_bom(infile)
1054
1055
1056
1057
1058 for line in infile:
1059 if (not line) or (line[-1] not in '\r\n'):
1060 continue
1061 for end in ('\r\n', '\n', '\r'):
1062 if line.endswith(end):
1063 self.newlines = end
1064 break
1065 break
1066 infile = [line.rstrip('\r\n') for line in infile]
1067
1068 self._parse(infile)
1069
1070 if self._errors:
1071 error = ConfigObjError("Parsing failed.")
1072
1073
1074 error.errors = self._errors
1075
1076 error.config = self
1077 raise error
1078
1079 del self._errors
1080
1081 if defaults['configspec'] is None:
1082 self.configspec = None
1083 else:
1084 self._handle_configspec(defaults['configspec'])
1085
1087 return 'ConfigObj({%s})' % ', '.join(
1088 [('%s: %s' % (repr(key), repr(self[key]))) for key in
1089 (self.scalars + self.sections)])
1090
1092 """
1093 Handle any BOM, and decode if necessary.
1094
1095 If an encoding is specified, that *must* be used - but the BOM should
1096 still be removed (and the BOM attribute set).
1097
1098 (If the encoding is wrongly specified, then a BOM for an alternative
1099 encoding won't be discovered or removed.)
1100
1101 If an encoding is not specified, UTF8 or UTF16 BOM will be detected and
1102 removed. The BOM attribute will be set. UTF16 will be decoded to
1103 unicode.
1104
1105 NOTE: This method must not be called with an empty ``infile``.
1106
1107 Specifying the *wrong* encoding is likely to cause a
1108 ``UnicodeDecodeError``.
1109
1110 ``infile`` must always be returned as a list of lines, but may be
1111 passed in as a single string.
1112 """
1113 if ((self.encoding is not None) and
1114 (self.encoding.lower() not in BOM_LIST)):
1115
1116
1117
1118 return self._decode(infile, self.encoding)
1119
1120 if isinstance(infile, (list, tuple)):
1121 line = infile[0]
1122 else:
1123 line = infile
1124 if self.encoding is not None:
1125
1126
1127
1128
1129 enc = BOM_LIST[self.encoding.lower()]
1130 if enc == 'utf_16':
1131
1132 for BOM, (encoding, final_encoding) in BOMS.items():
1133 if not final_encoding:
1134
1135 continue
1136 if infile.startswith(BOM):
1137
1138
1139
1140 return self._decode(infile, encoding)
1141
1142
1143
1144 return self._decode(infile, self.encoding)
1145
1146
1147 BOM = BOM_SET[enc]
1148 if not line.startswith(BOM):
1149 return self._decode(infile, self.encoding)
1150
1151 newline = line[len(BOM):]
1152
1153
1154 if isinstance(infile, (list, tuple)):
1155 infile[0] = newline
1156 else:
1157 infile = newline
1158 self.BOM = True
1159 return self._decode(infile, self.encoding)
1160
1161
1162 for BOM, (encoding, final_encoding) in BOMS.items():
1163 if not line.startswith(BOM):
1164 continue
1165 else:
1166
1167 self.encoding = final_encoding
1168 if not final_encoding:
1169 self.BOM = True
1170
1171
1172 newline = line[len(BOM):]
1173 if isinstance(infile, (list, tuple)):
1174 infile[0] = newline
1175 else:
1176 infile = newline
1177
1178 if isinstance(infile, StringTypes):
1179 return infile.splitlines(True)
1180 else:
1181 return infile
1182
1183 return self._decode(infile, encoding)
1184
1185
1186 if isinstance(infile, StringTypes):
1187
1188 return infile.splitlines(True)
1189 else:
1190 return infile
1191
1193 """Decode ascii strings to unicode if a self.encoding is specified."""
1194 if not self.encoding:
1195 return string
1196 else:
1197 return string.decode('ascii')
1198
1199 - def _decode(self, infile, encoding):
1200 """
1201 Decode infile to unicode. Using the specified encoding.
1202
1203 if is a string, it also needs converting to a list.
1204 """
1205 if isinstance(infile, StringTypes):
1206
1207
1208 return infile.decode(encoding).splitlines(True)
1209 for i, line in enumerate(infile):
1210 if not isinstance(line, unicode):
1211
1212
1213
1214 infile[i] = line.decode(encoding)
1215 return infile
1216
1218 """Decode element to unicode if necessary."""
1219 if not self.encoding:
1220 return line
1221 if isinstance(line, str) and self.default_encoding:
1222 return line.decode(self.default_encoding)
1223 return line
1224
1225 - def _str(self, value):
1226 """
1227 Used by ``stringify`` within validate, to turn non-string values
1228 into strings.
1229 """
1230 if not isinstance(value, StringTypes):
1231 return str(value)
1232 else:
1233 return value
1234
1236 """Actually parse the config file."""
1237 temp_list_values = self.list_values
1238 if self.unrepr:
1239 self.list_values = False
1240 comment_list = []
1241 done_start = False
1242 this_section = self
1243 maxline = len(infile) - 1
1244 cur_index = -1
1245 reset_comment = False
1246 while cur_index < maxline:
1247 if reset_comment:
1248 comment_list = []
1249 cur_index += 1
1250 line = infile[cur_index]
1251 sline = line.strip()
1252
1253 if not sline or sline.startswith('#'):
1254 reset_comment = False
1255 comment_list.append(line)
1256 continue
1257 if not done_start:
1258
1259 self.initial_comment = comment_list
1260 comment_list = []
1261 done_start = True
1262 reset_comment = True
1263
1264 mat = self._sectionmarker.match(line)
1265 if mat is not None:
1266
1267 (indent, sect_open, sect_name, sect_close, comment) = (
1268 mat.groups())
1269 if indent and (self.indent_type is None):
1270 self.indent_type = indent[0]
1271 cur_depth = sect_open.count('[')
1272 if cur_depth != sect_close.count(']'):
1273 self._handle_error(
1274 "Cannot compute the section depth at line %s.",
1275 NestingError, infile, cur_index)
1276 continue
1277 if cur_depth < this_section.depth:
1278
1279 try:
1280 parent = self._match_depth(
1281 this_section,
1282 cur_depth).parent
1283 except SyntaxError:
1284 self._handle_error(
1285 "Cannot compute nesting level at line %s.",
1286 NestingError, infile, cur_index)
1287 continue
1288 elif cur_depth == this_section.depth:
1289
1290 parent = this_section.parent
1291 elif cur_depth == this_section.depth + 1:
1292
1293 parent = this_section
1294 else:
1295 self._handle_error(
1296 "Section too nested at line %s.",
1297 NestingError, infile, cur_index)
1298
1299 sect_name = self._unquote(sect_name)
1300 if parent.has_key(sect_name):
1301 self._handle_error(
1302 'Duplicate section name at line %s.',
1303 DuplicateError, infile, cur_index)
1304 continue
1305
1306 this_section = Section(
1307 parent,
1308 cur_depth,
1309 self,
1310 name=sect_name)
1311 parent[sect_name] = this_section
1312 parent.inline_comments[sect_name] = comment
1313 parent.comments[sect_name] = comment_list
1314 continue
1315
1316
1317
1318 mat = self._keyword.match(line)
1319 if mat is not None:
1320
1321
1322 (indent, key, value) = mat.groups()
1323 if indent and (self.indent_type is None):
1324 self.indent_type = indent[0]
1325
1326 if value[:3] in ['"""', "'''"]:
1327 try:
1328 (value, comment, cur_index) = self._multiline(
1329 value, infile, cur_index, maxline)
1330 except SyntaxError:
1331 self._handle_error(
1332 'Parse error in value at line %s.',
1333 ParseError, infile, cur_index)
1334 continue
1335 else:
1336 if self.unrepr:
1337 value = unrepr(value)
1338 else:
1339
1340 try:
1341 (value, comment) = self._handle_value(value)
1342 except SyntaxError:
1343 self._handle_error(
1344 'Parse error in value at line %s.',
1345 ParseError, infile, cur_index)
1346 continue
1347
1348 key = self._unquote(key)
1349 if this_section.has_key(key):
1350 self._handle_error(
1351 'Duplicate keyword name at line %s.',
1352 DuplicateError, infile, cur_index)
1353 continue
1354
1355
1356
1357 this_section.__setitem__(key, value, unrepr=True)
1358 this_section.inline_comments[key] = comment
1359 this_section.comments[key] = comment_list
1360 continue
1361
1362
1363
1364 self._handle_error(
1365 'Invalid line at line "%s".',
1366 ParseError, infile, cur_index)
1367 if self.indent_type is None:
1368
1369 self.indent_type = ''
1370
1371 if not self and not self.initial_comment:
1372 self.initial_comment = comment_list
1373 elif not reset_comment:
1374 self.final_comment = comment_list
1375 self.list_values = temp_list_values
1376
1378 """
1379 Given a section and a depth level, walk back through the sections
1380 parents to see if the depth level matches a previous section.
1381
1382 Return a reference to the right section,
1383 or raise a SyntaxError.
1384 """
1385 while depth < sect.depth:
1386 if sect is sect.parent:
1387
1388 raise SyntaxError
1389 sect = sect.parent
1390 if sect.depth == depth:
1391 return sect
1392
1393 raise SyntaxError
1394
1396 """
1397 Handle an error according to the error settings.
1398
1399 Either raise the error or store it.
1400 The error will have occured at ``cur_index``
1401 """
1402 line = infile[cur_index]
1403 message = text % cur_index
1404 error = ErrorClass(message, cur_index, line)
1405 if self.raise_errors:
1406
1407 raise error
1408
1409
1410 self._errors.append(error)
1411
1413 """Return an unquoted version of a value"""
1414 if (value[0] == value[-1]) and (value[0] in ('"', "'")):
1415 value = value[1:-1]
1416 return value
1417
1418 - def _quote(self, value, multiline=True):
1419 """
1420 Return a safely quoted version of a value.
1421
1422 Raise a ConfigObjError if the value cannot be safely quoted.
1423 If multiline is ``True`` (default) then use triple quotes
1424 if necessary.
1425
1426 Don't quote values that don't need it.
1427 Recursively quote members of a list and return a comma joined list.
1428 Multiline is ``False`` for lists.
1429 Obey list syntax for empty and single member lists.
1430
1431 If ``list_values=False`` then the value is only quoted if it contains
1432 a ``\n`` (is multiline).
1433
1434 If ``write_empty_values`` is set, and the value is an empty string, it
1435 won't be quoted.
1436 """
1437 if multiline and self.write_empty_values and value == '':
1438
1439
1440 return ''
1441 if multiline and isinstance(value, (list, tuple)):
1442 if not value:
1443 return ','
1444 elif len(value) == 1:
1445 return self._quote(value[0], multiline=False) + ','
1446 return ', '.join([self._quote(val, multiline=False)
1447 for val in value])
1448 if not isinstance(value, StringTypes):
1449 if self.stringify:
1450 value = str(value)
1451 else:
1452 raise TypeError, 'Value "%s" is not a string.' % value
1453 squot = "'%s'"
1454 dquot = '"%s"'
1455 noquot = "%s"
1456 wspace_plus = ' \r\t\n\v\t\'"'
1457 tsquot = '"""%s"""'
1458 tdquot = "'''%s'''"
1459 if not value:
1460 return '""'
1461 if (not self.list_values and '\n' not in value) or not (multiline and
1462 ((("'" in value) and ('"' in value)) or ('\n' in value))):
1463 if not self.list_values:
1464
1465 quot = noquot
1466
1467 elif '\n' in value:
1468
1469 raise ConfigObjError, ('Value "%s" cannot be safely quoted.' %
1470 value)
1471 elif ((value[0] not in wspace_plus) and
1472 (value[-1] not in wspace_plus) and
1473 (',' not in value)):
1474 quot = noquot
1475 else:
1476 if ("'" in value) and ('"' in value):
1477 raise ConfigObjError, (
1478 'Value "%s" cannot be safely quoted.' % value)
1479 elif '"' in value:
1480 quot = squot
1481 else:
1482 quot = dquot
1483 else:
1484
1485 if (value.find('"""') != -1) and (value.find("'''") != -1):
1486 raise ConfigObjError, (
1487 'Value "%s" cannot be safely quoted.' % value)
1488 if value.find('"""') == -1:
1489 quot = tdquot
1490 else:
1491 quot = tsquot
1492 return quot % value
1493
1495 """
1496 Given a value string, unquote, remove comment,
1497 handle lists. (including empty and single member lists)
1498 """
1499
1500 if not self.list_values:
1501 mat = self._nolistvalue.match(value)
1502 if mat is None:
1503 raise SyntaxError
1504 (value, comment) = mat.groups()
1505 if not self.unrepr:
1506
1507 return (value, comment)
1508 else:
1509 return (unrepr(value), comment)
1510 mat = self._valueexp.match(value)
1511 if mat is None:
1512
1513
1514 raise SyntaxError
1515 (list_values, single, empty_list, comment) = mat.groups()
1516 if (list_values == '') and (single is None):
1517
1518 raise SyntaxError
1519
1520
1521 if empty_list is not None:
1522
1523 return ([], comment)
1524 if single is not None:
1525
1526 if list_values and not single:
1527
1528
1529 single = None
1530 else:
1531 single = single or '""'
1532 single = self._unquote(single)
1533 if list_values == '':
1534
1535 return (single, comment)
1536 the_list = self._listvalueexp.findall(list_values)
1537 the_list = [self._unquote(val) for val in the_list]
1538 if single is not None:
1539 the_list += [single]
1540 return (the_list, comment)
1541
1542 - def _multiline(self, value, infile, cur_index, maxline):
1543 """Extract the value, where we are in a multiline situation."""
1544 quot = value[:3]
1545 newvalue = value[3:]
1546 single_line = self._triple_quote[quot][0]
1547 multi_line = self._triple_quote[quot][1]
1548 mat = single_line.match(value)
1549 if mat is not None:
1550 retval = list(mat.groups())
1551 retval.append(cur_index)
1552 return retval
1553 elif newvalue.find(quot) != -1:
1554
1555 raise SyntaxError
1556
1557 while cur_index < maxline:
1558 cur_index += 1
1559 newvalue += '\n'
1560 line = infile[cur_index]
1561 if line.find(quot) == -1:
1562 newvalue += line
1563 else:
1564
1565 break
1566 else:
1567
1568 raise SyntaxError
1569 mat = multi_line.match(line)
1570 if mat is None:
1571
1572 raise SyntaxError
1573 (value, comment) = mat.groups()
1574 return (newvalue + value, comment, cur_index)
1575
1577 """Parse the configspec."""
1578
1579
1580 if not isinstance(configspec, ConfigObj):
1581 try:
1582 configspec = ConfigObj(
1583 configspec,
1584 raise_errors=True,
1585 file_error=True,
1586 list_values=False)
1587 except ConfigObjError, e:
1588
1589
1590 raise ConfigspecError('Parsing configspec failed: %s' % e)
1591 except IOError, e:
1592 raise IOError('Reading configspec failed: %s' % e)
1593 self._set_configspec_value(configspec, self)
1594
1596 """Used to recursively set configspec values."""
1597 if '__many__' in configspec.sections:
1598 section.configspec['__many__'] = configspec['__many__']
1599 if len(configspec.sections) > 1:
1600
1601 raise RepeatSectionError
1602 if hasattr(configspec, 'initial_comment'):
1603 section._configspec_initial_comment = configspec.initial_comment
1604 section._configspec_final_comment = configspec.final_comment
1605 section._configspec_encoding = configspec.encoding
1606 section._configspec_BOM = configspec.BOM
1607 section._configspec_newlines = configspec.newlines
1608 section._configspec_indent_type = configspec.indent_type
1609 for entry in configspec.scalars:
1610 section._configspec_comments[entry] = configspec.comments[entry]
1611 section._configspec_inline_comments[entry] = (
1612 configspec.inline_comments[entry])
1613 section.configspec[entry] = configspec[entry]
1614 section._order.append(entry)
1615 for entry in configspec.sections:
1616 if entry == '__many__':
1617 continue
1618 section._cs_section_comments[entry] = configspec.comments[entry]
1619 section._cs_section_inline_comments[entry] = (
1620 configspec.inline_comments[entry])
1621 if not section.has_key(entry):
1622 section[entry] = {}
1623 self._set_configspec_value(configspec[entry], section[entry])
1624
1626 """Dynamically assign configspec for repeated section."""
1627 try:
1628 section_keys = configspec.sections
1629 scalar_keys = configspec.scalars
1630 except AttributeError:
1631 section_keys = [entry for entry in configspec
1632 if isinstance(configspec[entry], dict)]
1633 scalar_keys = [entry for entry in configspec
1634 if not isinstance(configspec[entry], dict)]
1635 if '__many__' in section_keys and len(section_keys) > 1:
1636
1637 raise RepeatSectionError
1638 scalars = {}
1639 sections = {}
1640 for entry in scalar_keys:
1641 val = configspec[entry]
1642 scalars[entry] = val
1643 for entry in section_keys:
1644 val = configspec[entry]
1645 if entry == '__many__':
1646 scalars[entry] = val
1647 continue
1648 sections[entry] = val
1649
1650 section.configspec = scalars
1651 for entry in sections:
1652 if not section.has_key(entry):
1653 section[entry] = {}
1654 self._handle_repeat(section[entry], sections[entry])
1655
1656 - def _write_line(self, indent_string, entry, this_entry, comment):
1657 """Write an individual line, for the write method"""
1658
1659 if not self.unrepr:
1660 val = self._decode_element(self._quote(this_entry))
1661 else:
1662 val = repr(this_entry)
1663 return '%s%s%s%s%s' % (
1664 indent_string,
1665 self._decode_element(self._quote(entry, multiline=False)),
1666 self._a_to_u(' = '),
1667 val,
1668 self._decode_element(comment))
1669
1671 """Write a section marker line"""
1672 return '%s%s%s%s%s' % (
1673 indent_string,
1674 self._a_to_u('[' * depth),
1675 self._quote(self._decode_element(entry), multiline=False),
1676 self._a_to_u(']' * depth),
1677 self._decode_element(comment))
1678
1690
1692 """
1693 Compute the indent string, according to current indent_type and depth
1694 """
1695 if self.indent_type == '':
1696
1697 return ''
1698 if self.indent_type == '\t':
1699 return '\t' * depth
1700 if self.indent_type == ' ':
1701 return ' ' * NUM_INDENT_SPACES * depth
1702 raise SyntaxError
1703
1704
1705
1706 - def write(self, outfile=None, section=None):
1707 """
1708 Write the current ConfigObj as a file
1709
1710 tekNico: FIXME: use StringIO instead of real files
1711
1712 >>> filename = a.filename
1713 >>> a.filename = 'test.ini'
1714 >>> a.write()
1715 >>> a.filename = filename
1716 >>> a == ConfigObj('test.ini', raise_errors=True)
1717 1
1718 """
1719 if self.indent_type is None:
1720
1721 self.indent_type = DEFAULT_INDENT_TYPE
1722
1723 out = []
1724 cs = self._a_to_u('#')
1725 csp = self._a_to_u('# ')
1726 if section is None:
1727 int_val = self.interpolation
1728 self.interpolation = False
1729 section = self
1730 for line in self.initial_comment:
1731 line = self._decode_element(line)
1732 stripped_line = line.strip()
1733 if stripped_line and not stripped_line.startswith(cs):
1734 line = csp + line
1735 out.append(line)
1736
1737 indent_string = self._a_to_u(
1738 self._compute_indent_string(section.depth))
1739 for entry in (section.scalars + section.sections):
1740 if entry in section.defaults:
1741
1742 continue
1743 for comment_line in section.comments[entry]:
1744 comment_line = self._decode_element(comment_line.lstrip())
1745 if comment_line and not comment_line.startswith(cs):
1746 comment_line = csp + comment_line
1747 out.append(indent_string + comment_line)
1748 this_entry = section[entry]
1749 comment = self._handle_comment(section.inline_comments[entry])
1750
1751 if isinstance(this_entry, dict):
1752
1753 out.append(self._write_marker(
1754 indent_string,
1755 this_entry.depth,
1756 entry,
1757 comment))
1758 out.extend(self.write(section=this_entry))
1759 else:
1760 out.append(self._write_line(
1761 indent_string,
1762 entry,
1763 this_entry,
1764 comment))
1765
1766 if section is self:
1767 for line in self.final_comment:
1768 line = self._decode_element(line)
1769 stripped_line = line.strip()
1770 if stripped_line and not stripped_line.startswith(cs):
1771 line = csp + line
1772 out.append(line)
1773 self.interpolation = int_val
1774
1775 if section is not self:
1776 return out
1777
1778 if (self.filename is None) and (outfile is None):
1779
1780
1781
1782 if self.encoding:
1783 out = [l.encode(self.encoding) for l in out]
1784 if (self.BOM and ((self.encoding is None) or
1785 (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
1786
1787 if not out:
1788 out.append('')
1789 out[0] = BOM_UTF8 + out[0]
1790 return out
1791
1792
1793 output = (self._a_to_u(self.newlines or os.linesep)
1794 ).join(out)
1795 if self.encoding:
1796 output = output.encode(self.encoding)
1797 if (self.BOM and ((self.encoding is None) or
1798 (BOM_LIST.get(self.encoding.lower()) == 'utf_8'))):
1799
1800 output = BOM_UTF8 + output
1801 if outfile is not None:
1802 outfile.write(output)
1803 else:
1804 h = open(self.filename, 'wb')
1805 h.write(output)
1806 h.close()
1807
1808 - def validate(self, validator, preserve_errors=False, copy=False,
1809 section=None):
1810 """
1811 Test the ConfigObj against a configspec.
1812
1813 It uses the ``validator`` object from *validate.py*.
1814
1815 To run ``validate`` on the current ConfigObj, call: ::
1816
1817 test = config.validate(validator)
1818
1819 (Normally having previously passed in the configspec when the ConfigObj
1820 was created - you can dynamically assign a dictionary of checks to the
1821 ``configspec`` attribute of a section though).
1822
1823 It returns ``True`` if everything passes, or a dictionary of
1824 pass/fails (True/False). If every member of a subsection passes, it
1825 will just have the value ``True``. (It also returns ``False`` if all
1826 members fail).
1827
1828 In addition, it converts the values from strings to their native
1829 types if their checks pass (and ``stringify`` is set).
1830
1831 If ``preserve_errors`` is ``True`` (``False`` is default) then instead
1832 of a marking a fail with a ``False``, it will preserve the actual
1833 exception object. This can contain info about the reason for failure.
1834 For example the ``VdtValueTooSmallError`` indeicates that the value
1835 supplied was too small. If a value (or section) is missing it will
1836 still be marked as ``False``.
1837
1838 You must have the validate module to use ``preserve_errors=True``.
1839
1840 You can then use the ``flatten_errors`` function to turn your nested
1841 results dictionary into a flattened list of failures - useful for
1842 displaying meaningful error messages.
1843 """
1844 if section is None:
1845 if self.configspec is None:
1846 raise ValueError, 'No configspec supplied.'
1847 if preserve_errors:
1848 if VdtMissingValue is None:
1849 raise ImportError('Missing validate module.')
1850 section = self
1851
1852 spec_section = section.configspec
1853 if copy and hasattr(section, '_configspec_initial_comment'):
1854 section.initial_comment = section._configspec_initial_comment
1855 section.final_comment = section._configspec_final_comment
1856 section.encoding = section._configspec_encoding
1857 section.BOM = section._configspec_BOM
1858 section.newlines = section._configspec_newlines
1859 section.indent_type = section._configspec_indent_type
1860 if '__many__' in section.configspec:
1861 many = spec_section['__many__']
1862
1863
1864 for entry in section.sections:
1865 self._handle_repeat(section[entry], many)
1866
1867 out = {}
1868 ret_true = True
1869 ret_false = True
1870 order = [k for k in section._order if k in spec_section]
1871 order += [k for k in spec_section if k not in order]
1872 for entry in order:
1873 if entry == '__many__':
1874 continue
1875 if (not entry in section.scalars) or (entry in section.defaults):
1876
1877
1878 missing = True
1879 val = None
1880 if copy and not entry in section.scalars:
1881
1882 section.comments[entry] = (
1883 section._configspec_comments.get(entry, []))
1884 section.inline_comments[entry] = (
1885 section._configspec_inline_comments.get(entry, ''))
1886
1887 else:
1888 missing = False
1889 val = section[entry]
1890 try:
1891 check = validator.check(spec_section[entry],
1892 val,
1893 missing=missing
1894 )
1895 except validator.baseErrorClass, e:
1896 if not preserve_errors or isinstance(e, VdtMissingValue):
1897 out[entry] = False
1898 else:
1899
1900 out[entry] = e
1901 ret_false = False
1902 ret_true = False
1903 else:
1904 ret_false = False
1905 out[entry] = True
1906 if self.stringify or missing:
1907
1908
1909 if not self.stringify:
1910 if isinstance(check, (list, tuple)):
1911
1912 check = [self._str(item) for item in check]
1913 elif missing and check is None:
1914
1915 check = ''
1916 else:
1917 check = self._str(check)
1918 if (check != val) or missing:
1919 section[entry] = check
1920 if not copy and missing and entry not in section.defaults:
1921 section.defaults.append(entry)
1922
1923
1924
1925 for entry in section.sections:
1926
1927 if section is self and entry == 'DEFAULT':
1928 continue
1929 if copy:
1930 section.comments[entry] = section._cs_section_comments[entry]
1931 section.inline_comments[entry] = (
1932 section._cs_section_inline_comments[entry])
1933 check = self.validate(validator, preserve_errors=preserve_errors,
1934 copy=copy, section=section[entry])
1935 out[entry] = check
1936 if check == False:
1937 ret_true = False
1938 elif check == True:
1939 ret_false = False
1940 else:
1941 ret_true = False
1942 ret_false = False
1943
1944 if ret_true:
1945 return True
1946 elif ret_false:
1947 return False
1948 else:
1949 return out
1950
1952 """
1953 A simple validator.
1954 Can be used to check that all members expected are present.
1955
1956 To use it, provide a configspec with all your members in (the value given
1957 will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``
1958 method of your ``ConfigObj``. ``validate`` will return ``True`` if all
1959 members are present, or a dictionary with True/False meaning
1960 present/missing. (Whole missing sections will be replaced with ``False``)
1961 """
1962
1965
1966 - def check(self, check, member, missing=False):
1967 """A dummy check method, always returns the value unchanged."""
1968 if missing:
1969 raise self.baseErrorClass
1970 return member
1971
1972
1974 """
1975 An example function that will turn a nested dictionary of results
1976 (as returned by ``ConfigObj.validate``) into a flat list.
1977
1978 ``cfg`` is the ConfigObj instance being checked, ``res`` is the results
1979 dictionary returned by ``validate``.
1980
1981 (This is a recursive function, so you shouldn't use the ``levels`` or
1982 ``results`` arguments - they are used by the function.
1983
1984 Returns a list of keys that failed. Each member of the list is a tuple :
1985 ::
1986
1987 ([list of sections...], key, result)
1988
1989 If ``validate`` was called with ``preserve_errors=False`` (the default)
1990 then ``result`` will always be ``False``.
1991
1992 *list of sections* is a flattened list of sections that the key was found
1993 in.
1994
1995 If the section was missing then key will be ``None``.
1996
1997 If the value (or section) was missing then ``result`` will be ``False``.
1998
1999 If ``validate`` was called with ``preserve_errors=True`` and a value
2000 was present, but failed the check, then ``result`` will be the exception
2001 object returned. You can use this as a string that describes the failure.
2002
2003 For example *The value "3" is of the wrong type*.
2004
2005 >>> import validate
2006 >>> vtor = validate.Validator()
2007 >>> my_ini = '''
2008 ... option1 = True
2009 ... [section1]
2010 ... option1 = True
2011 ... [section2]
2012 ... another_option = Probably
2013 ... [section3]
2014 ... another_option = True
2015 ... [[section3b]]
2016 ... value = 3
2017 ... value2 = a
2018 ... value3 = 11
2019 ... '''
2020 >>> my_cfg = '''
2021 ... option1 = boolean()
2022 ... option2 = boolean()
2023 ... option3 = boolean(default=Bad_value)
2024 ... [section1]
2025 ... option1 = boolean()
2026 ... option2 = boolean()
2027 ... option3 = boolean(default=Bad_value)
2028 ... [section2]
2029 ... another_option = boolean()
2030 ... [section3]
2031 ... another_option = boolean()
2032 ... [[section3b]]
2033 ... value = integer
2034 ... value2 = integer
2035 ... value3 = integer(0, 10)
2036 ... [[[section3b-sub]]]
2037 ... value = string
2038 ... [section4]
2039 ... another_option = boolean()
2040 ... '''
2041 >>> cs = my_cfg.split('\\n')
2042 >>> ini = my_ini.split('\\n')
2043 >>> cfg = ConfigObj(ini, configspec=cs)
2044 >>> res = cfg.validate(vtor, preserve_errors=True)
2045 >>> errors = []
2046 >>> for entry in flatten_errors(cfg, res):
2047 ... section_list, key, error = entry
2048 ... section_list.insert(0, '[root]')
2049 ... if key is not None:
2050 ... section_list.append(key)
2051 ... else:
2052 ... section_list.append('[missing]')
2053 ... section_string = ', '.join(section_list)
2054 ... errors.append((section_string, ' = ', error))
2055 >>> errors.sort()
2056 >>> for entry in errors:
2057 ... print entry[0], entry[1], (entry[2] or 0)
2058 [root], option2 = 0
2059 [root], option3 = the value "Bad_value" is of the wrong type.
2060 [root], section1, option2 = 0
2061 [root], section1, option3 = the value "Bad_value" is of the wrong type.
2062 [root], section2, another_option = the value "Probably" is of the wrong type.
2063 [root], section3, section3b, section3b-sub, [missing] = 0
2064 [root], section3, section3b, value2 = the value "a" is of the wrong type.
2065 [root], section3, section3b, value3 = the value "11" is too big.
2066 [root], section4, [missing] = 0
2067 """
2068 if levels is None:
2069
2070 levels = []
2071 results = []
2072 if res is True:
2073 return results
2074 if res is False:
2075 results.append((levels[:], None, False))
2076 if levels:
2077 levels.pop()
2078 return results
2079 for (key, val) in res.items():
2080 if val == True:
2081 continue
2082 if isinstance(cfg.get(key), dict):
2083
2084 levels.append(key)
2085 flatten_errors(cfg[key], val, levels, results)
2086 continue
2087 results.append((levels[:], key, val))
2088
2089
2090 if levels:
2091 levels.pop()
2092
2093 return results
2094
2095 """*A programming language is a medium of expression.* - Paul Graham"""
2096