Results of a rewrite pass
[python/dscho.git] / Lib / ConfigParser.py
blob1fb9ec58d8fc372f0c41f353aa69eacf0b53a13c
1 """Configuration file parser.
3 A setup file consists of sections, lead by a "[section]" header,
4 and followed by "name: value" entries, with continuations and such in
5 the style of RFC 822.
7 The option values can contain format strings which refer to other values in
8 the same section, or values in a special [DEFAULT] section.
10 For example:
12 something: %(dir)s/whatever
14 would resolve the "%(dir)s" to the value of dir. All reference
15 expansions are done late, on demand.
17 Intrinsic defaults can be specified by passing them into the
18 ConfigParser constructor as a dictionary.
20 class:
22 ConfigParser -- responsible for for parsing a list of
23 configuration files, and managing the parsed database.
25 methods:
27 __init__(defaults=None)
28 create the parser and specify a dictionary of intrinsic defaults. The
29 keys must be strings, the values must be appropriate for %()s string
30 interpolation. Note that `__name__' is always an intrinsic default;
31 it's value is the section's name.
33 sections()
34 return all the configuration section names, sans DEFAULT
36 has_section(section)
37 return whether the given section exists
39 has_option(section, option)
40 return whether the given option exists in the given section
42 options(section)
43 return list of configuration options for the named section
45 read(filenames)
46 read and parse the list of named configuration files, given by
47 name. A single filename is also allowed. Non-existing files
48 are ignored.
50 readfp(fp, filename=None)
51 read and parse one configuration file, given as a file object.
52 The filename defaults to fp.name; it is only used in error
53 messages (if fp has no `name' attribute, the string `<???>' is used).
55 get(section, option, raw=False, vars=None)
56 return a string value for the named option. All % interpolations are
57 expanded in the return values, based on the defaults passed into the
58 constructor and the DEFAULT section. Additional substitutions may be
59 provided using the `vars' argument, which must be a dictionary whose
60 contents override any pre-existing defaults.
62 getint(section, options)
63 like get(), but convert value to an integer
65 getfloat(section, options)
66 like get(), but convert value to a float
68 getboolean(section, options)
69 like get(), but convert value to a boolean (currently case
70 insensitively defined as 0, false, no, off for False, and 1, true,
71 yes, on for True). Returns False or True.
73 items(section, raw=False, vars=None)
74 return a list of tuples with (name, value) for each option
75 in the section.
77 remove_section(section)
78 remove the given file section and all its options
80 remove_option(section, option)
81 remove the given option from the given section
83 set(section, option, value)
84 set the given option
86 write(fp)
87 write the configuration state in .ini format
88 """
90 import re
92 __all__ = ["NoSectionError", "DuplicateSectionError", "NoOptionError",
93 "InterpolationError", "InterpolationDepthError",
94 "InterpolationSyntaxError", "ParsingError",
95 "MissingSectionHeaderError", "ConfigParser",
96 "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"]
98 DEFAULTSECT = "DEFAULT"
100 MAX_INTERPOLATION_DEPTH = 10
104 # exception classes
105 class Error(Exception):
106 """Base class for ConfigParser exceptions."""
108 def __init__(self, msg=''):
109 self.message = msg
110 Exception.__init__(self, msg)
112 def __repr__(self):
113 return self.message
115 __str__ = __repr__
117 class NoSectionError(Error):
118 """Raised when no section matches a requested option."""
120 def __init__(self, section):
121 Error.__init__(self, 'No section: ' + `section`)
122 self.section = section
124 class DuplicateSectionError(Error):
125 """Raised when a section is multiply-created."""
127 def __init__(self, section):
128 Error.__init__(self, "Section %r already exists" % section)
129 self.section = section
131 class NoOptionError(Error):
132 """A requested option was not found."""
134 def __init__(self, option, section):
135 Error.__init__(self, "No option %r in section: %r" %
136 (option, section))
137 self.option = option
138 self.section = section
140 class InterpolationError(Error):
141 """Base class for interpolation-related exceptions."""
143 def __init__(self, option, section, msg):
144 Error.__init__(self, msg)
145 self.option = option
146 self.section = section
148 class InterpolationMissingOptionError(InterpolationError):
149 """A string substitution required a setting which was not available."""
151 def __init__(self, option, section, rawval, reference):
152 msg = ("Bad value substitution:\n"
153 "\tsection: [%s]\n"
154 "\toption : %s\n"
155 "\tkey : %s\n"
156 "\trawval : %s\n"
157 % (section, option, reference, rawval))
158 InterpolationError.__init__(self, option, section, msg)
159 self.reference = reference
161 class InterpolationSyntaxError(InterpolationError):
162 """Raised when the source text into which substitutions are made
163 does not conform to the required syntax."""
165 class InterpolationDepthError(InterpolationError):
166 """Raised when substitutions are nested too deeply."""
168 def __init__(self, option, section, rawval):
169 msg = ("Value interpolation too deeply recursive:\n"
170 "\tsection: [%s]\n"
171 "\toption : %s\n"
172 "\trawval : %s\n"
173 % (section, option, rawval))
174 InterpolationError.__init__(self, option, section, msg)
176 class ParsingError(Error):
177 """Raised when a configuration file does not follow legal syntax."""
179 def __init__(self, filename):
180 Error.__init__(self, 'File contains parsing errors: %s' % filename)
181 self.filename = filename
182 self.errors = []
184 def append(self, lineno, line):
185 self.errors.append((lineno, line))
186 self.message += '\n\t[line %2d]: %s' % (lineno, line)
188 class MissingSectionHeaderError(ParsingError):
189 """Raised when a key-value pair is found before any section header."""
191 def __init__(self, filename, lineno, line):
192 Error.__init__(
193 self,
194 'File contains no section headers.\nfile: %s, line: %d\n%s' %
195 (filename, lineno, line))
196 self.filename = filename
197 self.lineno = lineno
198 self.line = line
202 class RawConfigParser:
203 def __init__(self, defaults=None):
204 self._sections = {}
205 if defaults is None:
206 self._defaults = {}
207 else:
208 self._defaults = defaults
210 def defaults(self):
211 return self._defaults
213 def sections(self):
214 """Return a list of section names, excluding [DEFAULT]"""
215 # self._sections will never have [DEFAULT] in it
216 return self._sections.keys()
218 def add_section(self, section):
219 """Create a new section in the configuration.
221 Raise DuplicateSectionError if a section by the specified name
222 already exists.
224 if section in self._sections:
225 raise DuplicateSectionError(section)
226 self._sections[section] = {}
228 def has_section(self, section):
229 """Indicate whether the named section is present in the configuration.
231 The DEFAULT section is not acknowledged.
233 return section in self._sections
235 def options(self, section):
236 """Return a list of option names for the given section name."""
237 try:
238 opts = self._sections[section].copy()
239 except KeyError:
240 raise NoSectionError(section)
241 opts.update(self._defaults)
242 if '__name__' in opts:
243 del opts['__name__']
244 return opts.keys()
246 def read(self, filenames):
247 """Read and parse a filename or a list of filenames.
249 Files that cannot be opened are silently ignored; this is
250 designed so that you can specify a list of potential
251 configuration file locations (e.g. current directory, user's
252 home directory, systemwide directory), and all existing
253 configuration files in the list will be read. A single
254 filename may also be given.
256 if isinstance(filenames, basestring):
257 filenames = [filenames]
258 for filename in filenames:
259 try:
260 fp = open(filename)
261 except IOError:
262 continue
263 self._read(fp, filename)
264 fp.close()
266 def readfp(self, fp, filename=None):
267 """Like read() but the argument must be a file-like object.
269 The `fp' argument must have a `readline' method. Optional
270 second argument is the `filename', which if not given, is
271 taken from fp.name. If fp has no `name' attribute, `<???>' is
272 used.
275 if filename is None:
276 try:
277 filename = fp.name
278 except AttributeError:
279 filename = '<???>'
280 self._read(fp, filename)
282 def get(self, section, option):
283 opt = self.optionxform(option)
284 if section not in self._sections:
285 if section != DEFAULTSECT:
286 raise NoSectionError(section)
287 if opt in self._defaults:
288 return self._defaults[opt]
289 else:
290 raise NoOptionError(option, section)
291 elif opt in self._sections[section]:
292 return self._sections[section][opt]
293 elif opt in self._defaults:
294 return self._defaults[opt]
295 else:
296 raise NoOptionError(option, section)
298 def items(self, section):
299 try:
300 d2 = self._sections[section]
301 except KeyError:
302 if section != DEFAULTSECT:
303 raise NoSectionError(section)
304 d2 = {}
305 d = self._defaults.copy()
306 d.update(d2)
307 if "__name__" in d:
308 del d["__name__"]
309 return d.items()
311 def _get(self, section, conv, option):
312 return conv(self.get(section, option))
314 def getint(self, section, option):
315 return self._get(section, int, option)
317 def getfloat(self, section, option):
318 return self._get(section, float, option)
320 _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
321 '0': False, 'no': False, 'false': False, 'off': False}
323 def getboolean(self, section, option):
324 v = self.get(section, option)
325 if v.lower() not in self._boolean_states:
326 raise ValueError, 'Not a boolean: %s' % v
327 return self._boolean_states[v.lower()]
329 def optionxform(self, optionstr):
330 return optionstr.lower()
332 def has_option(self, section, option):
333 """Check for the existence of a given option in a given section."""
334 if not section or section == DEFAULTSECT:
335 option = self.optionxform(option)
336 return option in self._defaults
337 elif section not in self._sections:
338 return False
339 else:
340 option = self.optionxform(option)
341 return (option in self._sections[section]
342 or option in self._defaults)
344 def set(self, section, option, value):
345 """Set an option."""
346 if not section or section == DEFAULTSECT:
347 sectdict = self._defaults
348 else:
349 try:
350 sectdict = self._sections[section]
351 except KeyError:
352 raise NoSectionError(section)
353 sectdict[self.optionxform(option)] = value
355 def write(self, fp):
356 """Write an .ini-format representation of the configuration state."""
357 if self._defaults:
358 fp.write("[%s]\n" % DEFAULTSECT)
359 for (key, value) in self._defaults.items():
360 fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
361 fp.write("\n")
362 for section in self._sections:
363 fp.write("[%s]\n" % section)
364 for (key, value) in self._sections[section].items():
365 if key != "__name__":
366 fp.write("%s = %s\n" %
367 (key, str(value).replace('\n', '\n\t')))
368 fp.write("\n")
370 def remove_option(self, section, option):
371 """Remove an option."""
372 if not section or section == DEFAULTSECT:
373 sectdict = self._defaults
374 else:
375 try:
376 sectdict = self._sections[section]
377 except KeyError:
378 raise NoSectionError(section)
379 option = self.optionxform(option)
380 existed = option in sectdict
381 if existed:
382 del sectdict[option]
383 return existed
385 def remove_section(self, section):
386 """Remove a file section."""
387 existed = section in self._sections
388 if existed:
389 del self._sections[section]
390 return existed
393 # Regular expressions for parsing section headers and options.
395 SECTCRE = re.compile(
396 r'\[' # [
397 r'(?P<header>[^]]+)' # very permissive!
398 r'\]' # ]
400 OPTCRE = re.compile(
401 r'(?P<option>[^:=\s][^:=]*)' # very permissive!
402 r'\s*(?P<vi>[:=])\s*' # any number of space/tab,
403 # followed by separator
404 # (either : or =), followed
405 # by any # space/tab
406 r'(?P<value>.*)$' # everything up to eol
409 def _read(self, fp, fpname):
410 """Parse a sectioned setup file.
412 The sections in setup file contains a title line at the top,
413 indicated by a name in square brackets (`[]'), plus key/value
414 options lines, indicated by `name: value' format lines.
415 Continuations are represented by an embedded newline then
416 leading whitespace. Blank lines, lines beginning with a '#',
417 and just about everything else are ignored.
419 cursect = None # None, or a dictionary
420 optname = None
421 lineno = 0
422 e = None # None, or an exception
423 while True:
424 line = fp.readline()
425 if not line:
426 break
427 lineno = lineno + 1
428 # comment or blank line?
429 if line.strip() == '' or line[0] in '#;':
430 continue
431 if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR":
432 # no leading whitespace
433 continue
434 # continuation line?
435 if line[0].isspace() and cursect is not None and optname:
436 value = line.strip()
437 if value:
438 cursect[optname] = "%s\n%s" % (cursect[optname], value)
439 # a section header or option header?
440 else:
441 # is it a section header?
442 mo = self.SECTCRE.match(line)
443 if mo:
444 sectname = mo.group('header')
445 if sectname in self._sections:
446 cursect = self._sections[sectname]
447 elif sectname == DEFAULTSECT:
448 cursect = self._defaults
449 else:
450 cursect = {'__name__': sectname}
451 self._sections[sectname] = cursect
452 # So sections can't start with a continuation line
453 optname = None
454 # no section header in the file?
455 elif cursect is None:
456 raise MissingSectionHeaderError(fpname, lineno, `line`)
457 # an option line?
458 else:
459 mo = self.OPTCRE.match(line)
460 if mo:
461 optname, vi, optval = mo.group('option', 'vi', 'value')
462 if vi in ('=', ':') and ';' in optval:
463 # ';' is a comment delimiter only if it follows
464 # a spacing character
465 pos = optval.find(';')
466 if pos != -1 and optval[pos-1].isspace():
467 optval = optval[:pos]
468 optval = optval.strip()
469 # allow empty values
470 if optval == '""':
471 optval = ''
472 optname = self.optionxform(optname.rstrip())
473 cursect[optname] = optval
474 else:
475 # a non-fatal parsing error occurred. set up the
476 # exception but keep going. the exception will be
477 # raised at the end of the file and will contain a
478 # list of all bogus lines
479 if not e:
480 e = ParsingError(fpname)
481 e.append(lineno, `line`)
482 # if any parsing errors occurred, raise an exception
483 if e:
484 raise e
487 class ConfigParser(RawConfigParser):
489 def get(self, section, option, raw=False, vars=None):
490 """Get an option value for a given section.
492 All % interpolations are expanded in the return values, based on the
493 defaults passed into the constructor, unless the optional argument
494 `raw' is true. Additional substitutions may be provided using the
495 `vars' argument, which must be a dictionary whose contents overrides
496 any pre-existing defaults.
498 The section DEFAULT is special.
500 d = self._defaults.copy()
501 try:
502 d.update(self._sections[section])
503 except KeyError:
504 if section != DEFAULTSECT:
505 raise NoSectionError(section)
506 # Update with the entry specific variables
507 if vars is not None:
508 d.update(vars)
509 option = self.optionxform(option)
510 try:
511 value = d[option]
512 except KeyError:
513 raise NoOptionError(option, section)
515 if raw:
516 return value
517 else:
518 return self._interpolate(section, option, value, d)
520 def items(self, section, raw=False, vars=None):
521 """Return a list of tuples with (name, value) for each option
522 in the section.
524 All % interpolations are expanded in the return values, based on the
525 defaults passed into the constructor, unless the optional argument
526 `raw' is true. Additional substitutions may be provided using the
527 `vars' argument, which must be a dictionary whose contents overrides
528 any pre-existing defaults.
530 The section DEFAULT is special.
532 d = self._defaults.copy()
533 try:
534 d.update(self._sections[section])
535 except KeyError:
536 if section != DEFAULTSECT:
537 raise NoSectionError(section)
538 # Update with the entry specific variables
539 if vars:
540 d.update(vars)
541 options = d.keys()
542 if "__name__" in options:
543 options.remove("__name__")
544 if raw:
545 for option in options:
546 yield (option, d[option])
547 else:
548 for option in options:
549 yield (option,
550 self._interpolate(section, option, d[option], d))
552 def _interpolate(self, section, option, rawval, vars):
553 # do the string interpolation
554 value = rawval
555 depth = MAX_INTERPOLATION_DEPTH
556 while depth: # Loop through this until it's done
557 depth -= 1
558 if value.find("%(") != -1:
559 try:
560 value = value % vars
561 except KeyError, e:
562 raise InterpolationMissingOptionError(
563 option, section, rawval, e[0])
564 else:
565 break
566 if value.find("%(") != -1:
567 raise InterpolationDepthError(option, section, rawval)
568 return value
571 class SafeConfigParser(ConfigParser):
573 def _interpolate(self, section, option, rawval, vars):
574 # do the string interpolation
575 L = []
576 self._interpolate_some(option, L, rawval, section, vars, 1)
577 return ''.join(L)
579 _interpvar_match = re.compile(r"%\(([^)]+)\)s").match
581 def _interpolate_some(self, option, accum, rest, section, map, depth):
582 if depth > MAX_INTERPOLATION_DEPTH:
583 raise InterpolationDepthError(option, section, rest)
584 while rest:
585 p = rest.find("%")
586 if p < 0:
587 accum.append(rest)
588 return
589 if p > 0:
590 accum.append(rest[:p])
591 rest = rest[p:]
592 # p is no longer used
593 c = rest[1:2]
594 if c == "%":
595 accum.append("%")
596 rest = rest[2:]
597 elif c == "(":
598 m = self._interpvar_match(rest)
599 if m is None:
600 raise InterpolationSyntaxError(
601 "bad interpolation variable reference", rest)
602 var = m.group(1)
603 rest = rest[m.end():]
604 try:
605 v = map[var]
606 except KeyError:
607 raise InterpolationMissingOptionError(
608 option, section, rest, var)
609 if "%" in v:
610 self._interpolate_some(option, accum, v,
611 section, map, depth + 1)
612 else:
613 accum.append(v)
614 else:
615 raise InterpolationSyntaxError(
616 option, section, rest,
617 "'%' must be followed by '%' or '(', found: " + `rest`)