Merge lp:~debian-janitor/debian/sid/bzr/lintian-fixes into lp:~debian-bazaar/debian/sid/bzr/unstable

Proposed by Debian Janitor
Status: Merged
Merged at revision: 4232
Proposed branch: lp:~debian-janitor/debian/sid/bzr/lintian-fixes
Merge into: lp:~debian-bazaar/debian/sid/bzr/unstable
Diff against target: 2532 lines (+1/-2516)
2 files modified
debian/changelog (+1/-0)
debian/patches/02_external_configobj (+0/-2516)
To merge this branch: bzr merge lp:~debian-janitor/debian/sid/bzr/lintian-fixes
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Debian Janitor Pending
Review via email: mp+369707@code.launchpad.net

Commit message

Fix lintian issues: Remove patches missing from debian/patches/series.

Description of the change

Fix some issues reported by lintian
* Remove patches missing from debian/patches/series.

This merge proposal was created automatically by the Janitor bot
(https://janitor.debian.net/).

You can follow up to this merge proposal as you normally would.

Build and test logs for this branch can be found at
https://janitor.debian.net/pkg/bzr/34e36d4f-3d3c-43b8-84ad-18b03e6a9d13/.

To post a comment you must log in.
Jelmer Vernooij (jelmer) :
review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches