From 3d20986d9664f9d6025b1f8e89fa4b3804c0cbd6 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 9 Dec 1997 16:10:31 +0000 Subject: Checking in ConfigParser.py -- I don't see a reason why this can't be liberated. This was originally written by Ken and later revamped by Barry. --- Lib/ConfigParser.py | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 Lib/ConfigParser.py diff --git a/Lib/ConfigParser.py b/Lib/ConfigParser.py new file mode 100644 index 0000000..2f3ca51 --- /dev/null +++ b/Lib/ConfigParser.py @@ -0,0 +1,255 @@ +"""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 rfc822. + +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 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; it's value + is the section's name. + + sections() -- return all the configuration section names, sans DEFAULT + + options(section) -- return list of configuration options for the named + section + + read(*filenames) -- read and parse the list of named configuration files + + get(section, option, raw=0) -- 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. + + 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 defined as 0 + or 1, only) +""" + +import sys +import string +import regex +from types import ListType + + +SECTHEAD_RE = "^\[\([-A-Za-z0-9]*\)\][" + string.whitespace + "]*$" +secthead_cre = regex.compile(SECTHEAD_RE) +OPTION_RE = "^\([-A-Za-z0-9.]+\)\(:\|[" + string.whitespace + "]*=\)\(.*\)$" +option_cre = regex.compile(OPTION_RE) + +DEFAULTSECT = "DEFAULT" + + + +# exception classes +class Error: + def __init__(self, msg=''): + self.__msg = msg + def __repr__(self): + return self.__msg + +class NoSectionError(Error): + def __init__(self, section): + Error.__init__(self, 'No section: %s' % section) + self.section = section + +class DuplicateSectionError(Error): + def __init__(self, section): + Error.__init__(self, "Section %s already exists" % section) + self.section = section + +class NoOptionError(Error): + def __init__(self, option, section): + Error.__init__(self, "No option `%s' in section: %s" % + (option, section)) + self.option = option + self.section = section + +class InterpolationError(Error): + def __init__(self, reference, option, section): + Error.__init__(self, + "Bad value substitution: sect `%s', opt `%s', ref `%s'" + % (section, option, reference)) + self.reference = reference + self.option = option + self.section = section + + + +class ConfigParser: + def __init__(self, defaults=None): + self.__sections = {} + if defaults is None: + self.__defaults = {} + else: + self.__defaults = defaults + + 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 self.__sections.has_key(section): + 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 self.__sections.has_key(section) + + def options(self, section): + try: + opts = self.__sections[section].copy() + except KeyError: + raise NoSectionError(section) + opts.update(self.__defaults) + return opts.keys() + + def read(self, filenames): + """Read and parse a list of filenames.""" + if type(filenames) is type(''): + filenames = [filenames] + for file in filenames: + try: + fp = open(file, 'r') + self.__read(fp) + except IOError: + pass + + def get(self, section, option, raw=0): + """Get an option value for a given section. + + All % interpolations are expanded in the return values, based + on the defaults passed into the constructor. + + The section DEFAULT is special. + """ + try: + d = self.__sections[section].copy() + except KeyError: + if section == DEFAULTSECT: + d = {} + else: + raise NoSectionError(section) + d.update(self.__defaults) + option = string.lower(option) + try: + rawval = d[option] + except KeyError: + raise NoOptionError(option, section) + # do the string interpolation + if raw: + return rawval + try: + return rawval % d + except KeyError, key: + raise InterpolationError(key, option, section) + + def __get(self, section, conv, option): + return conv(self.get(section, option)) + + def getint(self, section, option): + return self.__get(section, string.atoi, option) + + def getfloat(self, section, option): + return self.__get(section, string.atof, option) + + def getboolean(self, section, option): + v = self.get(section, option) + val = string.atoi(v) + if val not in (0, 1): + raise ValueError, 'Not a boolean: %s' % v + return val + + def __read(self, fp): + """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. + """ + cursect = None # None, or a dictionary + optname = None + lineno = 0 + 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' \ + and line[0] == "r": # no leading whitespace + continue + # continuation line? + if line[0] in ' \t' and cursect <> None and optname: + value = string.strip(line) + if value: + cursect = cursect[optname] + '\n ' + value + # a section header? + elif secthead_cre.match(line) >= 0: + sectname = secthead_cre.group(1) + 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 + # an option line? + elif option_cre.match(line) >= 0: + optname, optval = option_cre.group(1, 3) + optname = string.lower(optname) + optval = string.strip(optval) + # allow empty values + if optval == '""': + optval = '' + cursect[optname] = optval + # an error + else: + print 'Error in %s at %d: %s', (fp.name, lineno, `line`) -- cgit v0.12