123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 |
- """Configuration file parser.
- A setup file consists of sections, lead by a "[section]" header,
- and followed by "name: value" entries, with continuations and such in
- the style of RFC 822.
- The option values can contain format strings which refer to other values in
- the same section, or values in a special [DEFAULT] section.
- For example:
- something: %(dir)s/whatever
- would resolve the "%(dir)s" to the value of dir. All reference
- expansions are done late, on demand.
- Intrinsic defaults can be specified by passing them into the
- ConfigParser constructor as a dictionary.
- class:
- ConfigParser -- responsible for parsing a list of
- configuration files, and managing the parsed database.
- methods:
- __init__(defaults=None)
- create the parser and specify a dictionary of intrinsic defaults. The
- keys must be strings, the values must be appropriate for %()s string
- interpolation. Note that `__name__' is always an intrinsic default;
- its value is the section's name.
- sections()
- return all the configuration section names, sans DEFAULT
- has_section(section)
- return whether the given section exists
- has_option(section, option)
- return whether the given option exists in the given section
- options(section)
- return list of configuration options for the named section
- read(filenames)
- read and parse the list of named configuration files, given by
- name. A single filename is also allowed. Non-existing files
- are ignored. Return list of successfully read files.
- readfp(fp, filename=None)
- read and parse one configuration file, given as a file object.
- The filename defaults to fp.name; it is only used in error
- messages (if fp has no `name' attribute, the string `<???>' is used).
- get(section, option, raw=False, vars=None)
- return a string value for the named option. All % interpolations are
- expanded in the return values, based on the defaults passed into the
- constructor and the DEFAULT section. Additional substitutions may be
- provided using the `vars' argument, which must be a dictionary whose
- contents override any pre-existing defaults.
- getint(section, options)
- like get(), but convert value to an integer
- getfloat(section, options)
- like get(), but convert value to a float
- getboolean(section, options)
- like get(), but convert value to a boolean (currently case
- insensitively defined as 0, false, no, off for False, and 1, true,
- yes, on for True). Returns False or True.
- items(section, raw=False, vars=None)
- return a list of tuples with (name, value) for each option
- in the section.
- remove_section(section)
- remove the given file section and all its options
- remove_option(section, option)
- remove the given option from the given section
- set(section, option, value)
- set the given option
- write(fp)
- write the configuration state in .ini format
- """
- import re
- __all__ = ["NoSectionError", "DuplicateSectionError", "NoOptionError",
- "InterpolationError", "InterpolationDepthError",
- "InterpolationSyntaxError", "ParsingError",
- "MissingSectionHeaderError",
- "ConfigParser", "SafeConfigParser", "RawConfigParser",
- "DEFAULTSECT", "MAX_INTERPOLATION_DEPTH"]
- DEFAULTSECT = "DEFAULT"
- MAX_INTERPOLATION_DEPTH = 10
- # exception classes
- class Error(Exception):
- """Base class for ConfigParser exceptions."""
- def __init__(self, msg=''):
- self.message = msg
- Exception.__init__(self, msg)
- def __repr__(self):
- return self.message
- __str__ = __repr__
- class NoSectionError(Error):
- """Raised when no section matches a requested option."""
- def __init__(self, section):
- Error.__init__(self, 'No section: %r' % (section,))
- self.section = section
- class DuplicateSectionError(Error):
- """Raised when a section is multiply-created."""
- def __init__(self, section):
- Error.__init__(self, "Section %r already exists" % section)
- self.section = section
- class NoOptionError(Error):
- """A requested option was not found."""
- def __init__(self, option, section):
- Error.__init__(self, "No option %r in section: %r" %
- (option, section))
- self.option = option
- self.section = section
- class InterpolationError(Error):
- """Base class for interpolation-related exceptions."""
- def __init__(self, option, section, msg):
- Error.__init__(self, msg)
- self.option = option
- self.section = section
- class InterpolationMissingOptionError(InterpolationError):
- """A string substitution required a setting which was not available."""
- def __init__(self, option, section, rawval, reference):
- msg = ("Bad value substitution:\n"
- "\tsection: [%s]\n"
- "\toption : %s\n"
- "\tkey : %s\n"
- "\trawval : %s\n"
- % (section, option, reference, rawval))
- InterpolationError.__init__(self, option, section, msg)
- self.reference = reference
- class InterpolationSyntaxError(InterpolationError):
- """Raised when the source text into which substitutions are made
- does not conform to the required syntax."""
- class InterpolationDepthError(InterpolationError):
- """Raised when substitutions are nested too deeply."""
- def __init__(self, option, section, rawval):
- msg = ("Value interpolation too deeply recursive:\n"
- "\tsection: [%s]\n"
- "\toption : %s\n"
- "\trawval : %s\n"
- % (section, option, rawval))
- InterpolationError.__init__(self, option, section, msg)
- class ParsingError(Error):
- """Raised when a configuration file does not follow legal syntax."""
- def __init__(self, filename):
- Error.__init__(self, 'File contains parsing errors: %s' % filename)
- self.filename = filename
- self.errors = []
- def append(self, lineno, line):
- self.errors.append((lineno, line))
- self.message += '\n\t[line %2d]: %s' % (lineno, line)
- class MissingSectionHeaderError(ParsingError):
- """Raised when a key-value pair is found before any section header."""
- def __init__(self, filename, lineno, line):
- Error.__init__(
- self,
- 'File contains no section headers.\nfile: %s, line: %d\n%r' %
- (filename, lineno, line))
- self.filename = filename
- self.lineno = lineno
- self.line = line
- class RawConfigParser:
- def __init__(self, defaults=None):
- self._sections = {}
- self._defaults = {}
- if defaults:
- for key, value in defaults.items():
- self._defaults[self.optionxform(key)] = value
- def defaults(self):
- return self._defaults
- def sections(self):
- """Return a list of section names, excluding [DEFAULT]"""
- # self._sections will never have [DEFAULT] in it
- return self._sections.keys()
- def add_section(self, section):
- """Create a new section in the configuration.
- Raise DuplicateSectionError if a section by the specified name
- already exists.
- """
- if section in self._sections:
- raise DuplicateSectionError(section)
- self._sections[section] = {}
- def has_section(self, section):
- """Indicate whether the named section is present in the configuration.
- The DEFAULT section is not acknowledged.
- """
- return section in self._sections
- def options(self, section):
- """Return a list of option names for the given section name."""
- try:
- opts = self._sections[section].copy()
- except KeyError:
- raise NoSectionError(section)
- opts.update(self._defaults)
- if '__name__' in opts:
- del opts['__name__']
- return opts.keys()
- def read(self, filenames):
- """Read and parse a filename or a list of filenames.
- Files that cannot be opened are silently ignored; this is
- designed so that you can specify a list of potential
- configuration file locations (e.g. current directory, user's
- home directory, systemwide directory), and all existing
- configuration files in the list will be read. A single
- filename may also be given.
- Return list of successfully read files.
- """
- if isinstance(filenames, basestring):
- filenames = [filenames]
- read_ok = []
- for filename in filenames:
- try:
- fp = open(filename)
- except IOError:
- continue
- self._read(fp, filename)
- fp.close()
- read_ok.append(filename)
- return read_ok
- def readfp(self, fp, filename=None):
- """Like read() but the argument must be a file-like object.
- The `fp' argument must have a `readline' method. Optional
- second argument is the `filename', which if not given, is
- taken from fp.name. If fp has no `name' attribute, `<???>' is
- used.
- """
- if filename is None:
- try:
- filename = fp.name
- except AttributeError:
- filename = '<???>'
- self._read(fp, filename)
- def get(self, section, option):
- opt = self.optionxform(option)
- if section not in self._sections:
- if section != DEFAULTSECT:
- raise NoSectionError(section)
- if opt in self._defaults:
- return self._defaults[opt]
- else:
- raise NoOptionError(option, section)
- elif opt in self._sections[section]:
- return self._sections[section][opt]
- elif opt in self._defaults:
- return self._defaults[opt]
- else:
- raise NoOptionError(option, section)
- def items(self, section):
- try:
- d2 = self._sections[section]
- except KeyError:
- if section != DEFAULTSECT:
- raise NoSectionError(section)
- d2 = {}
- d = self._defaults.copy()
- d.update(d2)
- if "__name__" in d:
- del d["__name__"]
- return d.items()
- def _get(self, section, conv, option):
- return conv(self.get(section, option))
- def getint(self, section, option):
- return self._get(section, int, option)
- def getfloat(self, section, option):
- return self._get(section, float, option)
- _boolean_states = {'1': True, 'yes': True, 'true': True, 'on': True,
- '0': False, 'no': False, 'false': False, 'off': False}
- def getboolean(self, section, option):
- v = self.get(section, option)
- if v.lower() not in self._boolean_states:
- raise ValueError, 'Not a boolean: %s' % v
- return self._boolean_states[v.lower()]
- def optionxform(self, optionstr):
- return optionstr.lower()
- def has_option(self, section, option):
- """Check for the existence of a given option in a given section."""
- if not section or section == DEFAULTSECT:
- option = self.optionxform(option)
- return option in self._defaults
- elif section not in self._sections:
- return False
- else:
- option = self.optionxform(option)
- return (option in self._sections[section]
- or option in self._defaults)
- def set(self, section, option, value):
- """Set an option."""
- if not section or section == DEFAULTSECT:
- sectdict = self._defaults
- else:
- try:
- sectdict = self._sections[section]
- except KeyError:
- raise NoSectionError(section)
- sectdict[self.optionxform(option)] = value
- def write(self, fp):
- """Write an .ini-format representation of the configuration state."""
- if self._defaults:
- fp.write("[%s]\n" % DEFAULTSECT)
- for (key, value) in self._defaults.items():
- fp.write("%s = %s\n" % (key, str(value).replace('\n', '\n\t')))
- fp.write("\n")
- for section in self._sections:
- fp.write("[%s]\n" % section)
- for (key, value) in self._sections[section].items():
- if key != "__name__":
- fp.write("%s = %s\n" %
- (key, str(value).replace('\n', '\n\t')))
- fp.write("\n")
- def remove_option(self, section, option):
- """Remove an option."""
- if not section or section == DEFAULTSECT:
- sectdict = self._defaults
- else:
- try:
- sectdict = self._sections[section]
- except KeyError:
- raise NoSectionError(section)
- option = self.optionxform(option)
- existed = option in sectdict
- if existed:
- del sectdict[option]
- return existed
- def remove_section(self, section):
- """Remove a file section."""
- existed = section in self._sections
- if existed:
- del self._sections[section]
- return existed
- #
- # Regular expressions for parsing section headers and options.
- #
- SECTCRE = re.compile(
- r'\[' # [
- r'(?P<header>[^]]+)' # very permissive!
- r'\]' # ]
- )
- OPTCRE = re.compile(
- r'(?P<option>[^:=\s][^:=]*)' # very permissive!
- r'\s*(?P<vi>[:=])\s*' # any number of space/tab,
- # followed by separator
- # (either : or =), followed
- # by any # space/tab
- r'(?P<value>.*)$' # everything up to eol
- )
- def _read(self, fp, fpname):
- """Parse a sectioned setup file.
- The sections in setup file contains a title line at the top,
- indicated by a name in square brackets (`[]'), plus key/value
- options lines, indicated by `name: value' format lines.
- Continuations are represented by an embedded newline then
- leading whitespace. Blank lines, lines beginning with a '#',
- and just about everything else are ignored.
- """
- cursect = None # None, or a dictionary
- optname = None
- lineno = 0
- e = None # None, or an exception
- while True:
- line = fp.readline()
- if not line:
- break
- lineno = lineno + 1
- # comment or blank line?
- if line.strip() == '' or line[0] in '#;':
- continue
- if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR":
- # no leading whitespace
- continue
- # continuation line?
- if line[0].isspace() and cursect is not None and optname:
- value = line.strip()
- if value:
- cursect[optname] = "%s\n%s" % (cursect[optname], value)
- # a section header or option header?
- else:
- # is it a section header?
- mo = self.SECTCRE.match(line)
- if mo:
- sectname = mo.group('header')
- if sectname in self._sections:
- cursect = self._sections[sectname]
- elif sectname == DEFAULTSECT:
- cursect = self._defaults
- else:
- cursect = {'__name__': sectname}
- self._sections[sectname] = cursect
- # So sections can't start with a continuation line
- optname = None
- # no section header in the file?
- elif cursect is None:
- raise MissingSectionHeaderError(fpname, lineno, line)
- # an option line?
- else:
- mo = self.OPTCRE.match(line)
- if mo:
- optname, vi, optval = mo.group('option', 'vi', 'value')
- if vi in ('=', ':') and ';' in optval:
- # ';' is a comment delimiter only if it follows
- # a spacing character
- pos = optval.find(';')
- if pos != -1 and optval[pos-1].isspace():
- optval = optval[:pos]
- optval = optval.strip()
- # allow empty values
- if optval == '""':
- optval = ''
- optname = self.optionxform(optname.rstrip())
- cursect[optname] = optval
- else:
- # a non-fatal parsing error occurred. set up the
- # exception but keep going. the exception will be
- # raised at the end of the file and will contain a
- # list of all bogus lines
- if not e:
- e = ParsingError(fpname)
- e.append(lineno, repr(line))
- # if any parsing errors occurred, raise an exception
- if e:
- raise e
- class ConfigParser(RawConfigParser):
- def get(self, section, option, raw=False, vars=None):
- """Get an option value for a given section.
- All % interpolations are expanded in the return values, based on the
- defaults passed into the constructor, unless the optional argument
- `raw' is true. Additional substitutions may be provided using the
- `vars' argument, which must be a dictionary whose contents overrides
- any pre-existing defaults.
- The section DEFAULT is special.
- """
- d = self._defaults.copy()
- try:
- d.update(self._sections[section])
- except KeyError:
- if section != DEFAULTSECT:
- raise NoSectionError(section)
- # Update with the entry specific variables
- if vars:
- for key, value in vars.items():
- d[self.optionxform(key)] = value
- option = self.optionxform(option)
- try:
- value = d[option]
- except KeyError:
- raise NoOptionError(option, section)
- if raw:
- return value
- else:
- return self._interpolate(section, option, value, d)
- def items(self, section, raw=False, vars=None):
- """Return a list of tuples with (name, value) for each option
- in the section.
- All % interpolations are expanded in the return values, based on the
- defaults passed into the constructor, unless the optional argument
- `raw' is true. Additional substitutions may be provided using the
- `vars' argument, which must be a dictionary whose contents overrides
- any pre-existing defaults.
- The section DEFAULT is special.
- """
- d = self._defaults.copy()
- try:
- d.update(self._sections[section])
- except KeyError:
- if section != DEFAULTSECT:
- raise NoSectionError(section)
- # Update with the entry specific variables
- if vars:
- for key, value in vars.items():
- d[self.optionxform(key)] = value
- options = d.keys()
- if "__name__" in options:
- options.remove("__name__")
- if raw:
- return [(option, d[option])
- for option in options]
- else:
- return [(option, self._interpolate(section, option, d[option], d))
- for option in options]
- def _interpolate(self, section, option, rawval, vars):
- # do the string interpolation
- value = rawval
- depth = MAX_INTERPOLATION_DEPTH
- while depth: # Loop through this until it's done
- depth -= 1
- if "%(" in value:
- value = self._KEYCRE.sub(self._interpolation_replace, value)
- try:
- value = value % vars
- except KeyError, e:
- raise InterpolationMissingOptionError(
- option, section, rawval, e[0])
- else:
- break
- if "%(" in value:
- raise InterpolationDepthError(option, section, rawval)
- return value
- _KEYCRE = re.compile(r"%\(([^)]*)\)s|.")
- def _interpolation_replace(self, match):
- s = match.group(1)
- if s is None:
- return match.group()
- else:
- return "%%(%s)s" % self.optionxform(s)
- class SafeConfigParser(ConfigParser):
- def _interpolate(self, section, option, rawval, vars):
- # do the string interpolation
- L = []
- self._interpolate_some(option, L, rawval, section, vars, 1)
- return ''.join(L)
- _interpvar_match = re.compile(r"%\(([^)]+)\)s").match
- def _interpolate_some(self, option, accum, rest, section, map, depth):
- if depth > MAX_INTERPOLATION_DEPTH:
- raise InterpolationDepthError(option, section, rest)
- while rest:
- p = rest.find("%")
- if p < 0:
- accum.append(rest)
- return
- if p > 0:
- accum.append(rest[:p])
- rest = rest[p:]
- # p is no longer used
- c = rest[1:2]
- if c == "%":
- accum.append("%")
- rest = rest[2:]
- elif c == "(":
- m = self._interpvar_match(rest)
- if m is None:
- raise InterpolationSyntaxError(option, section,
- "bad interpolation variable reference %r" % rest)
- var = self.optionxform(m.group(1))
- rest = rest[m.end():]
- try:
- v = map[var]
- except KeyError:
- raise InterpolationMissingOptionError(
- option, section, rest, var)
- if "%" in v:
- self._interpolate_some(option, accum, v,
- section, map, depth + 1)
- else:
- accum.append(v)
- else:
- raise InterpolationSyntaxError(
- option, section,
- "'%%' must be followed by '%%' or '(', found: %r" % (rest,))
- def set(self, section, option, value):
- """Set an option. Extend ConfigParser.set: check for string values."""
- if not isinstance(value, basestring):
- raise TypeError("option values must be strings")
- ConfigParser.set(self, section, option, value)
|