| """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 | |
| """ | |
| try: | |
| from collections import OrderedDict as _default_dict | |
| except ImportError: | |
| # fallback for setup.py which hasn't yet built _collections | |
| _default_dict = dict | |
| 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 _get_message(self): | |
| """Getter for 'message'; needed only to override deprecation in | |
| BaseException.""" | |
| return self.__message | |
| def _set_message(self, value): | |
| """Setter for 'message'; needed only to override deprecation in | |
| BaseException.""" | |
| self.__message = value | |
| # BaseException.message has been deprecated since Python 2.6. To prevent | |
| # DeprecationWarning from popping up over this pre-existing attribute, use | |
| # a new property that takes lookup precedence. | |
| message = property(_get_message, _set_message) | |
| 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, dict_type=_default_dict, | |
| allow_no_value=False): | |
| self._dict = dict_type | |
| self._sections = self._dict() | |
| self._defaults = self._dict() | |
| if allow_no_value: | |
| self._optcre = self.OPTCRE_NV | |
| else: | |
| self._optcre = self.OPTCRE | |
| 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. Raise ValueError if name is DEFAULT or any of it's | |
| case-insensitive variants. | |
| """ | |
| if section.lower() == "default": | |
| raise ValueError, 'Invalid section name: %s' % section | |
| if section in self._sections: | |
| raise DuplicateSectionError(section) | |
| self._sections[section] = self._dict() | |
| 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 = self._dict() | |
| 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=None): | |
| """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__": | |
| continue | |
| if (value is not None) or (self._optcre == self.OPTCRE): | |
| key = " = ".join((key, str(value).replace('\n', '\n\t'))) | |
| fp.write("%s\n" % (key)) | |
| 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 | |
| ) | |
| OPTCRE_NV = re.compile( | |
| r'(?P<option>[^:=\s][^:=]*)' # very permissive! | |
| r'\s*(?:' # any number of space/tab, | |
| r'(?P<vi>[:=])\s*' # optionally 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].append(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 = self._dict() | |
| 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') | |
| optname = self.optionxform(optname.rstrip()) | |
| # This check is fine because the OPTCRE cannot | |
| # match if it would set optval to None | |
| if optval is not None: | |
| 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 = '' | |
| cursect[optname] = [optval] | |
| else: | |
| # valueless option handling | |
| 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 | |
| # join the multi-line values collected while reading | |
| all_sections = [self._defaults] | |
| all_sections.extend(self._sections.values()) | |
| for options in all_sections: | |
| for name, val in options.items(): | |
| if isinstance(val, list): | |
| options[name] = '\n'.join(val) | |
| import UserDict as _UserDict | |
| class _Chainmap(_UserDict.DictMixin): | |
| """Combine multiple mappings for successive lookups. | |
| For example, to emulate Python's normal lookup sequence: | |
| import __builtin__ | |
| pylookup = _Chainmap(locals(), globals(), vars(__builtin__)) | |
| """ | |
| def __init__(self, *maps): | |
| self._maps = maps | |
| def __getitem__(self, key): | |
| for mapping in self._maps: | |
| try: | |
| return mapping[key] | |
| except KeyError: | |
| pass | |
| raise KeyError(key) | |
| def keys(self): | |
| result = [] | |
| seen = set() | |
| for mapping in self_maps: | |
| for key in mapping: | |
| if key not in seen: | |
| result.append(key) | |
| seen.add(key) | |
| return result | |
| class ConfigParser(RawConfigParser): | |
| def get(self, section, option, raw=False, vars=None): | |
| """Get an option value for a given section. | |
| If `vars' is provided, it must be a dictionary. The option is looked up | |
| in `vars' (if provided), `section', and in `defaults' in that order. | |
| All % interpolations are expanded in the return values, unless the | |
| optional argument `raw' is true. Values for interpolation keys are | |
| looked up in the same manner as the option. | |
| The section DEFAULT is special. | |
| """ | |
| sectiondict = {} | |
| try: | |
| sectiondict = self._sections[section] | |
| except KeyError: | |
| if section != DEFAULTSECT: | |
| raise NoSectionError(section) | |
| # Update with the entry specific variables | |
| vardict = {} | |
| if vars: | |
| for key, value in vars.items(): | |
| vardict[self.optionxform(key)] = value | |
| d = _Chainmap(vardict, sectiondict, self._defaults) | |
| option = self.optionxform(option) | |
| try: | |
| value = d[option] | |
| except KeyError: | |
| raise NoOptionError(option, section) | |
| if raw or value is None: | |
| 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 value and "%(" in value: | |
| value = self._KEYCRE.sub(self._interpolation_replace, value) | |
| try: | |
| value = value % vars | |
| except KeyError, e: | |
| raise InterpolationMissingOptionError( | |
| option, section, rawval, e.args[0]) | |
| else: | |
| break | |
| if value and "%(" 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_re = re.compile(r"%\(([^)]+)\)s") | |
| 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_re.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=None): | |
| """Set an option. Extend ConfigParser.set: check for string values.""" | |
| # The only legal non-string value if we allow valueless | |
| # options is None, so we need to check if the value is a | |
| # string if: | |
| # - we do not allow valueless options, or | |
| # - we allow valueless options but the value is not None | |
| if self._optcre is self.OPTCRE or value: | |
| if not isinstance(value, basestring): | |
| raise TypeError("option values must be strings") | |
| if value is not None: | |
| # check for bad percent signs: | |
| # first, replace all "good" interpolations | |
| tmp_value = value.replace('%%', '') | |
| tmp_value = self._interpvar_re.sub('', tmp_value) | |
| # then, check if there's a lone percent sign left | |
| if '%' in tmp_value: | |
| raise ValueError("invalid interpolation syntax in %r at " | |
| "position %d" % (value, tmp_value.find('%'))) | |
| ConfigParser.set(self, section, option, value) |