1 """Configuration file parser.
2
3 *******************************************************************************
4 Slightly modified version of the ConfigParser module from the Standard Library,
5 Python 2.3. The modifications are to stop the INI files from being utterly
6 mangled when writing back to them. Specifically the aim is to preserve the
7 ordering of sections, and the options within them, thereby allowing the
8 INI file to be structured in a consistent, logical manner for human-
9 readability. Later versions may also preserve comments to some extent.
10
11 ConfigParserRT ('Round Trip') is a fully compatible drop-in replacement for the
12 standard ConfigParser module, the only differences are in the formatting of
13 written INI files.
14
15 Anthony Horton
16 horton@ast.cam.ac.uk
17 *******************************************************************************
18
19 A setup file consists of sections, lead by a "[section]" header,
20 and followed by "name: value" entries, with continuations and such in
21 the style of RFC 822.
22
23 The option values can contain format strings which refer to other values in
24 the same section, or values in a special [DEFAULT] section.
25
26 For example:
27
28 something: %(dir)s/whatever
29
30 would resolve the "%(dir)s" to the value of dir. All reference
31 expansions are done late, on demand.
32
33 Intrinsic defaults can be specified by passing them into the
34 ConfigParser constructor as a dictionary.
35
36 class:
37
38 ConfigParser -- responsible for parsing a list of
39 configuration files, and managing the parsed database.
40
41 methods:
42
43 __init__(defaults=None)
44 create the parser and specify a dictionary of intrinsic defaults. The
45 keys must be strings, the values must be appropriate for %()s string
46 interpolation. Note that `__name__' is always an intrinsic default;
47 it's value is the section's name.
48
49 sections()
50 return all the configuration section names, sans DEFAULT
51
52 has_section(section)
53 return whether the given section exists
54
55 has_option(section, option)
56 return whether the given option exists in the given section
57
58 options(section)
59 return list of configuration options for the named section
60
61 read(filenames)
62 read and parse the list of named configuration files, given by
63 name. A single filename is also allowed. Non-existing files
64 are ignored.
65
66 readfp(fp, filename=None)
67 read and parse one configuration file, given as a file object.
68 The filename defaults to fp.name; it is only used in error
69 messages (if fp has no `name' attribute, the string `<???>' is used).
70
71 get(section, option, raw=False, vars=None)
72 return a string value for the named option. All % interpolations are
73 expanded in the return values, based on the defaults passed into the
74 constructor and the DEFAULT section. Additional substitutions may be
75 provided using the `vars' argument, which must be a dictionary whose
76 contents override any pre-existing defaults.
77
78 getint(section, options)
79 like get(), but convert value to an integer
80
81 getfloat(section, options)
82 like get(), but convert value to a float
83
84 getboolean(section, options)
85 like get(), but convert value to a boolean (currently case
86 insensitively defined as 0, false, no, off for False, and 1, true,
87 yes, on for True). Returns False or True.
88
89 items(section, raw=False, vars=None)
90 return a list of tuples with (name, value) for each option
91 in the section.
92
93 remove_section(section)
94 remove the given file section and all its options
95
96 remove_option(section, option)
97 remove the given option from the given section
98
99 set(section, option, value)
100 set the given option
101
102 write(fp)
103 write the configuration state in .ini format
104 """
105
106 import re
107 from opdict import opdict
108
109 __all__ = ["NoSectionError", "DuplicateSectionError", "NoOptionError",
110 "InterpolationError", "InterpolationDepthError",
111 "InterpolationSyntaxError", "ParsingError",
112 "MissingSectionHeaderError", "ConfigParser", "SafeConfigParser",
113 "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"]
114
115 DEFAULTSECT = "DEFAULT"
116
117 MAX_INTERPOLATION_DEPTH = 10
118
119
120
121
123 """Base class for ConfigParser exceptions."""
124
126 self.message = msg
127 Exception.__init__(self, msg)
128
130 return self.message
131
132 __str__ = __repr__
133
135 """Raised when no section matches a requested option."""
136
138 Error.__init__(self, 'No section: ' + `section`)
139 self.section = section
140
142 """Raised when a section is multiply-created."""
143
145 Error.__init__(self, "Section %r already exists" % section)
146 self.section = section
147
149 """A requested option was not found."""
150
152 Error.__init__(self, "No option %r in section: %r" %
153 (option, section))
154 self.option = option
155 self.section = section
156
158 """Base class for interpolation-related exceptions."""
159
160 - def __init__(self, option, section, msg):
161 Error.__init__(self, msg)
162 self.option = option
163 self.section = section
164
166 """A string substitution required a setting which was not available."""
167
168 - def __init__(self, option, section, rawval, reference):
169 msg = ("Bad value substitution:\n"
170 "\tsection: [%s]\n"
171 "\toption : %s\n"
172 "\tkey : %s\n"
173 "\trawval : %s\n"
174 % (section, option, reference, rawval))
175 InterpolationError.__init__(self, option, section, msg)
176 self.reference = reference
177
179 """Raised when the source text into which substitutions are made
180 does not conform to the required syntax."""
181
183 """Raised when substitutions are nested too deeply."""
184
185 - def __init__(self, option, section, rawval):
186 msg = ("Value interpolation too deeply recursive:\n"
187 "\tsection: [%s]\n"
188 "\toption : %s\n"
189 "\trawval : %s\n"
190 % (section, option, rawval))
191 InterpolationError.__init__(self, option, section, msg)
192
194 """Raised when a configuration file does not follow legal syntax."""
195
200
201 - def append(self, lineno, line):
202 self.errors.append((lineno, line))
203 self.message += '\n\t[line %2d]: %s' % (lineno, line)
204
206 """Raised when a key-value pair is found before any section header."""
207
209 Error.__init__(
210 self,
211 'File contains no section headers.\nfile: %s, line: %d\n%s' %
212 (filename, lineno, line))
213 self.filename = filename
214 self.lineno = lineno
215 self.line = line
216
217
218
228
230 return self._defaults
231
233 """Return a list of section names, excluding [DEFAULT]"""
234
235 return self._sections.keys()
236
238 """Create a new section in the configuration.
239
240 Raise DuplicateSectionError if a section by the specified name
241 already exists.
242 """
243 if section in self._sections:
244 raise DuplicateSectionError(section)
245
246 self._section[section] = opdict()
247
249 """Indicate whether the named section is present in the configuration.
250
251 The DEFAULT section is not acknowledged.
252 """
253 return section in self._sections
254
256 """Return a list of option names for the given section name."""
257 try:
258 opts = self._sections[section].copy()
259 except KeyError:
260 raise NoSectionError(section)
261 opts.update(self._defaults)
262 if '__name__' in opts:
263 del opts['__name__']
264 return opts.keys()
265
266 - def read(self, filenames):
267 """Read and parse a filename or a list of filenames.
268
269 Files that cannot be opened are silently ignored; this is
270 designed so that you can specify a list of potential
271 configuration file locations (e.g. current directory, user's
272 home directory, systemwide directory), and all existing
273 configuration files in the list will be read. A single
274 filename may also be given.
275 """
276 if isinstance(filenames, basestring):
277 filenames = [filenames]
278 for filename in filenames:
279 try:
280 fp = open(filename)
281 except IOError:
282 continue
283 self._read(fp, filename)
284 fp.close()
285
286 - def readfp(self, fp, filename=None):
287 """Like read() but the argument must be a file-like object.
288
289 The `fp' argument must have a `readline' method. Optional
290 second argument is the `filename', which if not given, is
291 taken from fp.name. If fp has no `name' attribute, `<???>' is
292 used.
293
294 """
295 if filename is None:
296 try:
297 filename = fp.name
298 except AttributeError:
299 filename = '<???>'
300 self._read(fp, filename)
301
302 - def get(self, section, option):
303 opt = self.optionxform(option)
304 if section not in self._sections:
305 if section != DEFAULTSECT:
306 raise NoSectionError(section)
307 if opt in self._defaults:
308 return self._defaults[opt]
309 else:
310 raise NoOptionError(option, section)
311 elif opt in self._sections[section]:
312 return self._sections[section][opt]
313 elif opt in self._defaults:
314 return self._defaults[opt]
315 else:
316 raise NoOptionError(option, section)
317
318 - def items(self, section):
319 try:
320 d2 = self._sections[section]
321 except KeyError:
322 if section != DEFAULTSECT:
323 raise NoSectionError(section)
324
325 d2 = opdict()
326 d = self._defaults.copy()
327 d.update(d2)
328 if "__name__" in d:
329 del d["__name__"]
330 return d.items()
331
332 - def _get(self, section, conv, option):
333 return conv(self.get(section, option))
334
335 - def getint(self, section, option):
336 return self._get(section, int, option)
337
339 return self._get(section, float, option)
340
341 _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
342 '0': False, 'no': False, 'false': False, 'off': False}
343
349
352
354 """Check for the existence of a given option in a given section."""
355 if not section or section == DEFAULTSECT:
356 option = self.optionxform(option)
357 return option in self._defaults
358 elif section not in self._sections:
359 return False
360 else:
361 option = self.optionxform(option)
362 return (option in self._sections[section]
363 or option in self._defaults)
364
365 - def set(self, section, option, value):
366 """Set an option."""
367 if not section or section == DEFAULTSECT:
368 sectdict = self._defaults
369 else:
370 try:
371 sectdict = self._sections[section]
372 except KeyError:
373 raise NoSectionError(section)
374 sectdict[self.optionxform(option)] = value
375
377 """Write an .ini-format representation of the configuration state."""
378 if self._defaults:
379 fp.write("[%s]\n" % DEFAULTSECT)
380 for (key, value) in self._defaults.items():
381 fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
382 fp.write("\n")
383 for section in self._sections:
384 fp.write("[%s]\n" % section)
385 for (key, value) in self._sections[section].items():
386 if key != "__name__":
387 fp.write("%s = %s\n" %
388 (key, str(value).replace('\n', '\n\t')))
389 fp.write("\n")
390
392 """Remove an option."""
393 if not section or section == DEFAULTSECT:
394 sectdict = self._defaults
395 else:
396 try:
397 sectdict = self._sections[section]
398 except KeyError:
399 raise NoSectionError(section)
400 option = self.optionxform(option)
401 existed = option in sectdict
402 if existed:
403 del sectdict[option]
404 return existed
405
407 """Remove a file section."""
408 existed = section in self._sections
409 if existed:
410 del self._sections[section]
411
412 self._sect_list.remove(section)
413 return existed
414
415
416
417
418 SECTCRE = re.compile(
419 r'\['
420 r'(?P<header>[^]]+)'
421 r'\]'
422 )
423 OPTCRE = re.compile(
424 r'(?P<option>[^:=\s][^:=]*)'
425 r'\s*(?P<vi>[:=])\s*'
426
427
428
429 r'(?P<value>.*)$'
430 )
431
432 - def _read(self, fp, fpname):
433 """Parse a sectioned setup file.
434
435 The sections in setup file contains a title line at the top,
436 indicated by a name in square brackets (`[]'), plus key/value
437 options lines, indicated by `name: value' format lines.
438 Continuations are represented by an embedded newline then
439 leading whitespace. Blank lines, lines beginning with a '#',
440 and just about everything else are ignored.
441 """
442 cursect = None
443 optname = None
444 lineno = 0
445 e = None
446 while True:
447 line = fp.readline()
448 if not line:
449 break
450 lineno = lineno + 1
451
452 if line.strip() == '' or line[0] in '#;':
453 continue
454 if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR":
455
456 continue
457
458 if line[0].isspace() and cursect is not None and optname:
459 value = line.strip()
460 if value:
461 cursect[optname] = "%s\n%s" % (cursect[optname], value)
462
463 else:
464
465 mo = self.SECTCRE.match(line)
466 if mo:
467 sectname = mo.group('header')
468 if sectname in self._sections:
469 cursect = self._sections[sectname]
470 elif sectname == DEFAULTSECT:
471 cursect = self._defaults
472 else:
473
474 cursect = opdict({'__name__': sectname})
475 self._sections[sectname] = cursect
476
477 optname = None
478
479 elif cursect is None:
480 raise MissingSectionHeaderError(fpname, lineno, `line`)
481
482 else:
483 mo = self.OPTCRE.match(line)
484 if mo:
485 optname, vi, optval = mo.group('option', 'vi', 'value')
486 if vi in ('=', ':') and ';' in optval:
487
488
489 pos = optval.find(';')
490 if pos != -1 and optval[pos-1].isspace():
491 optval = optval[:pos]
492 optval = optval.strip()
493
494 if optval == '""':
495 optval = ''
496 optname = self.optionxform(optname.rstrip())
497 cursect[optname] = optval
498 else:
499
500
501
502
503 if not e:
504 e = ParsingError(fpname)
505 e.append(lineno, `line`)
506
507 if e:
508 raise e
509
510
512
513 - def get(self, section, option, raw=False, vars=None):
514 """Get an option value for a given section.
515
516 All % interpolations are expanded in the return values, based on the
517 defaults passed into the constructor, unless the optional argument
518 `raw' is true. Additional substitutions may be provided using the
519 `vars' argument, which must be a dictionary whose contents overrides
520 any pre-existing defaults.
521
522 The section DEFAULT is special.
523 """
524 d = self._defaults.copy()
525 try:
526 d.update(self._sections[section])
527 except KeyError:
528 if section != DEFAULTSECT:
529 raise NoSectionError(section)
530
531 if vars is not None:
532 d.update(vars)
533 option = self.optionxform(option)
534 try:
535 value = d[option]
536 except KeyError:
537 raise NoOptionError(option, section)
538
539 if raw:
540 return value
541 else:
542 return self._interpolate(section, option, value, d)
543
544 - def items(self, section, raw=False, vars=None):
545 """Return a list of tuples with (name, value) for each option
546 in the section.
547
548 All % interpolations are expanded in the return values, based on the
549 defaults passed into the constructor, unless the optional argument
550 `raw' is true. Additional substitutions may be provided using the
551 `vars' argument, which must be a dictionary whose contents overrides
552 any pre-existing defaults.
553
554 The section DEFAULT is special.
555 """
556 d = self._defaults.copy()
557 try:
558 d.update(self._sections[section])
559 except KeyError:
560 if section != DEFAULTSECT:
561 raise NoSectionError(section)
562
563 if vars:
564 d.update(vars)
565 options = d.keys()
566 if "__name__" in options:
567 options.remove("__name__")
568 if raw:
569 return [(option, d[option])
570 for option in options]
571 else:
572 return [(option, self._interpolate(section, option, d[option], d))
573 for option in options]
574
592
593
595
597
598 L = []
599 self._interpolate_some(option, L, rawval, section, vars, 1)
600 return ''.join(L)
601
602 _interpvar_match = re.compile(r"%\(([^)]+)\)s").match
603
605 if depth > MAX_INTERPOLATION_DEPTH:
606 raise InterpolationDepthError(option, section, rest)
607 while rest:
608 p = rest.find("%")
609 if p < 0:
610 accum.append(rest)
611 return
612 if p > 0:
613 accum.append(rest[:p])
614 rest = rest[p:]
615
616 c = rest[1:2]
617 if c == "%":
618 accum.append("%")
619 rest = rest[2:]
620 elif c == "(":
621 m = self._interpvar_match(rest)
622 if m is None:
623 raise InterpolationSyntaxError(option, section,
624 "bad interpolation variable reference %r" % rest)
625 var = m.group(1)
626 rest = rest[m.end():]
627 try:
628 v = map[var]
629 except KeyError:
630 raise InterpolationMissingOptionError(
631 option, section, rest, var)
632 if "%" in v:
633 self._interpolate_some(option, accum, v,
634 section, map, depth + 1)
635 else:
636 accum.append(v)
637 else:
638 raise InterpolationSyntaxError(
639 option, section,
640 "'%' must be followed by '%' or '(', found: " + `rest`)
641