Merge lp:~debian-janitor/debian/sid/bzr/lintian-fixes into lp:~debian-bazaar/debian/sid/bzr/unstable
- Sid (98)
- lintian-fixes
- Merge into 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 |
Related bugs: |
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/
Description of the change
Fix some issues reported by lintian
* Remove patches missing from debian/
This merge proposal was created automatically by the Janitor bot
(https:/
You can follow up to this merge proposal as you normally would.
Build and test logs for this branch can be found at
https:/
To post a comment you must log in.
Revision history for this message
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 | - |