cpython/Lib/dos-8x3/configpa.py

470 lines
16 KiB
Python
Raw Normal View History

1997-12-09 12:56:41 -04:00
"""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
1998-08-11 23:38:11 -03:00
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.
1997-12-09 12:56:41 -04:00
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 for parsing a list of
configuration files, and managing the parsed database.
methods:
1999-02-09 14:40:13 -04:00
__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;
it's value is the section's name.
1997-12-09 12:56:41 -04:00
1999-02-09 14:40:13 -04:00
sections()
return all the configuration section names, sans DEFAULT
1997-12-09 12:56:41 -04:00
2000-05-08 14:31:04 -03:00
has_section(section)
return whether the given section exists
2000-09-01 16:25:51 -03:00
has_option(section, option)
return whether the given option exists in the given section
1999-02-09 14:40:13 -04:00
options(section)
return list of configuration options for the named section
1997-12-09 12:56:41 -04:00
2000-05-08 14:31:04 -03:00
has_option(section, option)
return whether the given section has the given option
1999-02-09 14:40:13 -04:00
read(filenames)
2000-05-08 14:31:04 -03:00
read and parse the list of named configuration files, given by
name. A single filename is also allowed. Non-existing files
are ignored.
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).
1997-12-09 12:56:41 -04:00
1999-02-09 14:40:13 -04:00
get(section, option, raw=0, 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.
1997-12-09 12:56:41 -04:00
1999-02-09 14:40:13 -04:00
getint(section, options)
like get(), but convert value to an integer
1997-12-09 12:56:41 -04:00
1999-02-09 14:40:13 -04:00
getfloat(section, options)
like get(), but convert value to a float
1997-12-09 12:56:41 -04:00
1999-02-09 14:40:13 -04:00
getboolean(section, options)
like get(), but convert value to a boolean (currently defined as 0 or
1, only)
2000-09-01 16:25:51 -03:00
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
1997-12-09 12:56:41 -04:00
"""
import sys
import string
1998-08-11 23:38:11 -03:00
import re
1997-12-09 12:56:41 -04:00
DEFAULTSECT = "DEFAULT"
2000-10-09 19:14:43 -03:00
MAX_INTERPOLATION_DEPTH = 10
1997-12-09 12:56:41 -04:00
# exception classes
class Error:
def __init__(self, msg=''):
1998-08-11 23:38:11 -03:00
self._msg = msg
1997-12-09 12:56:41 -04:00
def __repr__(self):
1998-08-11 23:38:11 -03:00
return self._msg
1997-12-09 12:56:41 -04:00
class NoSectionError(Error):
def __init__(self, section):
1998-03-26 18:14:20 -04:00
Error.__init__(self, 'No section: %s' % section)
self.section = section
1997-12-09 12:56:41 -04:00
class DuplicateSectionError(Error):
def __init__(self, section):
1998-03-26 18:14:20 -04:00
Error.__init__(self, "Section %s already exists" % section)
self.section = section
1997-12-09 12:56:41 -04:00
class NoOptionError(Error):
def __init__(self, option, section):
1998-03-26 18:14:20 -04:00
Error.__init__(self, "No option `%s' in section: %s" %
(option, section))
self.option = option
self.section = section
1997-12-09 12:56:41 -04:00
class InterpolationError(Error):
1998-08-11 23:38:11 -03:00
def __init__(self, reference, option, section, rawval):
1998-03-26 18:14:20 -04:00
Error.__init__(self,
1998-08-11 23:38:11 -03:00
"Bad value substitution:\n"
"\tsection: [%s]\n"
"\toption : %s\n"
"\tkey : %s\n"
"\trawval : %s\n"
% (section, option, reference, rawval))
1998-03-26 18:14:20 -04:00
self.reference = reference
self.option = option
self.section = section
1997-12-09 12:56:41 -04:00
2000-10-09 19:14:43 -03:00
class InterpolationDepthError(Error):
def __init__(self, option, section, rawval):
Error.__init__(self,
"Value interpolation too deeply recursive:\n"
"\tsection: [%s]\n"
"\toption : %s\n"
"\trawval : %s\n"
% (section, option, rawval))
self.option = option
self.section = section
1998-08-11 23:38:11 -03:00
class ParsingError(Error):
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._msg = self._msg + '\n\t[line %2d]: %s' % (lineno, line)
2000-10-09 19:14:43 -03:00
class MissingSectionHeaderError(ParsingError):
def __init__(self, filename, lineno, line):
Error.__init__(
self,
'File contains no section headers.\nfile: %s, line: %d\n%s' %
(filename, lineno, line))
self.filename = filename
self.lineno = lineno
self.line = line
1997-12-09 12:56:41 -04:00
class ConfigParser:
def __init__(self, defaults=None):
1998-03-26 18:14:20 -04:00
self.__sections = {}
if defaults is None:
self.__defaults = {}
else:
self.__defaults = defaults
1997-12-09 12:56:41 -04:00
def defaults(self):
1998-03-26 18:14:20 -04:00
return self.__defaults
1997-12-09 12:56:41 -04:00
def sections(self):
1998-03-26 18:14:20 -04:00
"""Return a list of section names, excluding [DEFAULT]"""
# self.__sections will never have [DEFAULT] in it
return self.__sections.keys()
1997-12-09 12:56:41 -04:00
def add_section(self, section):
1998-03-26 18:14:20 -04:00
"""Create a new section in the configuration.
1997-12-09 12:56:41 -04:00
1998-03-26 18:14:20 -04:00
Raise DuplicateSectionError if a section by the specified name
already exists.
"""
if self.__sections.has_key(section):
raise DuplicateSectionError(section)
self.__sections[section] = {}
1997-12-09 12:56:41 -04:00
def has_section(self, section):
1998-03-26 18:14:20 -04:00
"""Indicate whether the named section is present in the configuration.
1997-12-09 12:56:41 -04:00
1998-03-26 18:14:20 -04:00
The DEFAULT section is not acknowledged.
"""
2000-10-09 19:14:43 -03:00
return section in self.sections()
1997-12-09 12:56:41 -04:00
def options(self, section):
2000-05-08 14:31:04 -03:00
"""Return a list of option names for the given section name."""
1998-03-26 18:14:20 -04:00
try:
opts = self.__sections[section].copy()
except KeyError:
raise NoSectionError(section)
opts.update(self.__defaults)
2000-10-09 19:14:43 -03:00
if opts.has_key('__name__'):
del opts['__name__']
1998-03-26 18:14:20 -04:00
return opts.keys()
1997-12-09 12:56:41 -04:00
2000-05-08 14:31:04 -03:00
def has_option(self, section, option):
"""Return whether the given section has the given option."""
2000-10-09 19:14:43 -03:00
return option in self.options(section)
2000-05-08 14:31:04 -03:00
1997-12-09 12:56:41 -04:00
def read(self, filenames):
2000-05-08 14:31:04 -03:00
"""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.
"""
2000-06-29 16:35:29 -03:00
if type(filenames) in [type(''), type(u'')]:
1998-03-26 18:14:20 -04:00
filenames = [filenames]
2000-05-08 14:31:04 -03:00
for filename in filenames:
1998-03-26 18:14:20 -04:00
try:
2000-05-08 14:31:04 -03:00
fp = open(filename)
1998-03-26 18:14:20 -04:00
except IOError:
2000-05-08 14:31:04 -03:00
continue
self.__read(fp, filename)
fp.close()
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)
1997-12-09 12:56:41 -04:00
1999-02-09 14:40:13 -04:00
def get(self, section, option, raw=0, vars=None):
1998-03-26 18:14:20 -04:00
"""Get an option value for a given section.
1999-02-09 14:40:13 -04:00
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.
1998-03-26 18:14:20 -04:00
The section DEFAULT is special.
"""
try:
sectdict = self.__sections[section].copy()
except KeyError:
if section == DEFAULTSECT:
sectdict = {}
else:
raise NoSectionError(section)
d = self.__defaults.copy()
d.update(sectdict)
1999-02-09 14:40:13 -04:00
# Update with the entry specific variables
if vars:
d.update(vars)
2000-05-08 14:31:04 -03:00
option = self.optionxform(option)
1998-03-26 18:14:20 -04:00
try:
rawval = d[option]
except KeyError:
raise NoOptionError(option, section)
2000-10-09 19:14:43 -03:00
1998-03-26 18:14:20 -04:00
if raw:
return rawval
1997-12-09 12:56:41 -04:00
2000-10-09 19:14:43 -03:00
# do the string interpolation
1999-02-09 14:40:13 -04:00
value = rawval # Make it a pretty variable name
1999-02-16 16:05:35 -04:00
depth = 0
while depth < 10: # Loop through this until it's done
depth = depth + 1
2000-05-08 14:31:04 -03:00
if string.find(value, "%(") >= 0:
1999-02-09 14:40:13 -04:00
try:
value = value % d
except KeyError, key:
raise InterpolationError(key, option, section, rawval)
else:
2000-10-09 19:14:43 -03:00
break
if value.find("%(") >= 0:
raise InterpolationDepthError(option, section, rawval)
return value
1999-02-09 14:40:13 -04:00
1997-12-09 12:56:41 -04:00
def __get(self, section, conv, option):
1998-03-26 18:14:20 -04:00
return conv(self.get(section, option))
1997-12-09 12:56:41 -04:00
def getint(self, section, option):
1998-03-26 18:14:20 -04:00
return self.__get(section, string.atoi, option)
1997-12-09 12:56:41 -04:00
def getfloat(self, section, option):
1998-03-26 18:14:20 -04:00
return self.__get(section, string.atof, option)
1997-12-09 12:56:41 -04:00
def getboolean(self, section, option):
1998-03-26 18:14:20 -04:00
v = self.get(section, option)
val = string.atoi(v)
if val not in (0, 1):
raise ValueError, 'Not a boolean: %s' % v
return val
1997-12-09 12:56:41 -04:00
2000-05-08 14:31:04 -03:00
def optionxform(self, optionstr):
return string.lower(optionstr)
2000-09-01 16:25:51 -03:00
def has_option(self, section, option):
"""Check for the existence of a given option in a given section."""
if not section or section == "DEFAULT":
return self.__defaults.has_key(option)
elif not self.has_section(section):
return 0
else:
return self.__sections[section].has_key(option)
def set(self, section, option, value):
"""Set an option."""
if not section or section == "DEFAULT":
sectdict = self.__defaults
else:
try:
sectdict = self.__sections[section]
except KeyError:
raise NoSectionError(section)
sectdict[option] = value
def write(self, fp):
"""Write an .ini-format representation of the configuration state."""
if self.__defaults:
fp.write("[DEFAULT]\n")
for (key, value) in self.__defaults.items():
fp.write("%s = %s\n" % (key, value))
fp.write("\n")
for section in self.sections():
fp.write("[" + section + "]\n")
sectdict = self.__sections[section]
for (key, value) in sectdict.items():
if key == "__name__":
continue
fp.write("%s = %s\n" % (key, value))
fp.write("\n")
def remove_option(self, section, option):
"""Remove an option."""
if not section or section == "DEFAULT":
sectdict = self.__defaults
else:
try:
sectdict = self.__sections[section]
except KeyError:
raise NoSectionError(section)
existed = sectdict.has_key(key)
if existed:
del sectdict[key]
return existed
def remove_section(self, section):
"""Remove a file section."""
if self.__sections.has_key(section):
del self.__sections[section]
return 1
else:
return 0
1998-08-11 23:38:11 -03:00
#
# Regular expressions for parsing section headers and options. Note a
# slight semantic change from the previous version, because of the use
# of \w, _ is allowed in section header names.
2000-05-08 14:31:04 -03:00
SECTCRE = re.compile(
1998-08-11 23:38:11 -03:00
r'\[' # [
2000-10-09 19:14:43 -03:00
r'(?P<header>[-\w_.*,(){} ]+)' # a lot of stuff found by IvL
1998-08-11 23:38:11 -03:00
r'\]' # ]
)
2000-05-08 14:31:04 -03:00
OPTCRE = re.compile(
r'(?P<option>[-\w_.*,(){}]+)' # a lot of stuff found by IvL
r'[ \t]*(?P<vi>[:=])[ \t]*' # any number of space/tab,
1998-08-11 23:38:11 -03:00
# followed by separator
# (either : or =), followed
# by any # space/tab
r'(?P<value>.*)$' # everything up to eol
)
2000-05-08 14:31:04 -03:00
def __read(self, fp, fpname):
1998-03-26 18:14:20 -04:00
"""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.
Continuation are represented by an embedded newline then
leading whitespace. Blank lines, lines beginning with a '#',
and just about everything else is ignored.
"""
1998-08-11 23:38:11 -03:00
cursect = None # None, or a dictionary
1998-03-26 18:14:20 -04:00
optname = None
lineno = 0
1998-08-11 23:38:11 -03:00
e = None # None, or an exception
1998-03-26 18:14:20 -04:00
while 1:
line = fp.readline()
if not line:
break
lineno = lineno + 1
# comment or blank line?
if string.strip(line) == '' or line[0] in '#;':
continue
if string.lower(string.split(line)[0]) == 'rem' \
2000-05-08 14:31:04 -03:00
and line[0] in "rR": # no leading whitespace
1998-03-26 18:14:20 -04:00
continue
# continuation line?
1998-08-11 23:38:11 -03:00
if line[0] in ' \t' and cursect is not None and optname:
1998-03-26 18:14:20 -04:00
value = string.strip(line)
if value:
1998-08-11 23:38:11 -03:00
cursect[optname] = cursect[optname] + '\n ' + value
# a section header or option header?
1998-03-26 18:14:20 -04:00
else:
1998-08-11 23:38:11 -03:00
# is it a section header?
2000-05-08 14:31:04 -03:00
mo = self.SECTCRE.match(line)
1998-08-11 23:38:11 -03:00
if mo:
sectname = mo.group('header')
if self.__sections.has_key(sectname):
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:
2000-05-08 14:31:04 -03:00
raise MissingSectionHeaderError(fpname, lineno, `line`)
1998-08-11 23:38:11 -03:00
# an option line?
else:
2000-05-08 14:31:04 -03:00
mo = self.OPTCRE.match(line)
1998-08-11 23:38:11 -03:00
if mo:
2000-05-08 14:31:04 -03:00
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 = string.find(optval, ';')
if pos and optval[pos-1] in string.whitespace:
optval = optval[:pos]
1998-08-11 23:38:11 -03:00
optval = string.strip(optval)
# allow empty values
if optval == '""':
optval = ''
2000-09-26 14:32:27 -03:00
cursect[self.optionxform(optname)] = optval
1998-08-11 23:38:11 -03:00
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:
2000-05-08 14:31:04 -03:00
e = ParsingError(fpname)
1998-08-11 23:38:11 -03:00
e.append(lineno, `line`)
# if any parsing errors occurred, raise an exception
if e:
raise e