Source code for src.tools.config_parser

"""New tools for parsing configuration files."""

import ConfigParser as cp
import re

FORMAT_BASIC = 0
FORMAT_REPR = 1
FORMAT_AUTO = 2
_BOOLEAN_STATES = {'true': True,
                   'yes': True,
                   '1': True,
                   1: True,
                   'false': False,
                   'no': False,
                   '0': False,
                   0: False}

[docs]class ConfigParser(object): """A class to parse configuration files in a variety of ways. Parameters ---------- filePath : string The absolute path to the configuration file. fileFormat : int The flag indicating the format of the configuration file. Options are the following: FORMAT_BASIC The file is treated as a standard configuration file. All return values are strings, unless one of the specialized accessor methods is used. FORMAT_REPR The file consists of entries formatted according to __repr__, so that they can be cast back to the appropriate types using __eval__. FORMAT_AUTO The parser attempts to guess the format of the entries based on syntax. substitutions : dict A dictionary whose keys are strings. The values will be substituted into the file wherever the corresponding key is referenced (using the standard rules for string formatting). defaultValues : dict A dictionaries whose keys are tuples of strings. The first element of each tuple should be a section name, and the second element should be an option name. Whenever a section and option is requested, not found in the configuration file, and found in `defaultValues`, the value from `defaultValues` will be returned and written to the file in the appropriate place. preserveCase : bool Whether the names of the sections and options should preserve case. """ def __init__(self, filePath, fileFormat, substitutions=None, defaultValues=None, preserveCase=True): self._filePath = filePath self._fileFormat = fileFormat self._defaultValues = {} if defaultValues is not None: for item in defaultValues: section, option = item if section not in self._defaultValues: self._defaultValues[section] = {option: defaultValues[item]} else: self._defaultValues[section][option] = defaultValues[item] if substitutions is None: self._configParser = cp.ConfigParser() else: self._configParser = cp.ConfigParser(substitutions) if preserveCase: self._configParser.optionxform = str self._configParser.read(self._filePath)
[docs] def getSections(self): """Return a list of available sections. Returns ------- list of str A list of strings specifying the sections included in both the configuration file and the dictionary of default values. """ answer = self._configParser.sections() for item in self._defaultValues: if item not in answer: answer.append(item) return answer
[docs] def getOptions(self, section): """Return a list of options under a specified section. Return a list of strings specifying the keys contained within a specified section, including the information from both the defaults dictionary and the configuration file. Each option will occur only once. Parameters ---------- section : str The section whose options should be retrieved. Returns ------- list of str A list of strings indicating the options listed under the specified section. """ answer = None if self._configParser.has_section(section): answer = self._configParser.options(section) if section in self._defaultValues: if answer is None: answer = [] for item in self._defaultValues[section]: if item not in answer: answer.append(item) return answer
[docs] def getOptionsDict(self, section): """Return a dictionary containing the options and values in a section. Parameters ---------- section : str The section whose values should be returned. Returns ------- dict A dictionary containing the names of the options in the specified section and the values associated with those options. """ answer = {} for option in self.getOptions(section): answer[option] = self.get(section, option) return answer
[docs] def get(self, section, option, default=None, converter=None): """Read a value from the configuration file. Attempt to read a value from the configuration file under the section `section` and associated with the key `option`. If either of these is missing from the file, search the dictionary of default values. If either the section or the key is missing from that dictionary, also, return `default`. If the file format is FORMAT_BASIC and the requested item is found in the file, return the `string` from the file, passed through `converter` if it is specified. If the file format is FORMAT_REPR and the requested item is found in the file, return the value from the file, passed first through `eval` and then, if it is specified, through `converter`. If the file format is FORMAT_AUTO and the requested item is found in the file, attempt to guess the type of the data in the file, cast the data to that type, and return it, passing it through `converter` if it is specified. If the requested data is not found in the file, return the appropriate element from the dictionary of default values, passed through `converter` if it is specified. If the requested data is not found in either the file or the defaults dictionary, return `default`, passed through `converter` if it is specified. Finally, after any/all conversions have taken place, write the value that will be returned to the configuration file and return said value. In summary, - `converter`, if specified, takes precedence over all other data type casting; and - the search order is (1) the configuration file, (2) the dictionary of default values, and (3) the default supplied to this method. Parameters ---------- section : string The section from which the value should be read. option : string The item within the specified section whose value should be read. default : (variant) The value to return if the specified section or option does not exist in either the file or the dictionary of default values. converter : function A function to convert the value to the appropriate type. Returns ------- (variant) The value associated with `section` and `option` in the configuration file. """ changed = False if not self._configParser.has_section(section): self._configParser.add_section(section) changed = True if self._configParser.has_option(section, option): value = self._configParser.get(section, option) elif section in self._defaultValues: if option in self._defaultValues[section]: value = self._defaultValues[section][option] else: value = default changed = True else: value = default changed = True if self._fileFormat == FORMAT_REPR: value = eval(value) elif self._fileFormat == FORMAT_AUTO: value = _parseSequence(value) if converter is not None: value = converter(value) changed = True if changed: self.set(section, option, value) return value
[docs] def getInt(self, section, option, default=0): """Return a value from the configuration file as an integer. Parameters ---------- section : string The section from which the value should be read. option : string The item within the specified section whose value should be read. default : int The value to return if the specified section or option does not exist in either the file or the dictionary of default values. Returns ------- int The integer associated with the specified section and key, or `default` if no such entry exists. """ return self.get(section, option, default, int)
[docs] def getFloat(self, section, option, default=0): """Return a value from the configuration file as a float. Parameters ---------- section : string The section from which the value should be read. option : string The item within the specified section whose value should be read. default : float The value to return if the specified section or option does not exist in either the file or the dictionary of default values. Returns ------- int The float associated with the specified section and key, or `default` if no such entry exists. """ return self.get(section, option, default, float)
[docs] def getBoolean(self, section, option, default=False): """Return a value from the configuration file as a boolean. Parameters ---------- section : string The section from which the value should be read. option : string The item within the specified section whose value should be read. default : bool The value to return if the specified section or option does not exist in either the file or the dictionary of default values. Returns ------- bool The boolean associated with the specified section and key, or `default` if no such entry exists. """ return self.get(section, option, default, _bool)
[docs] def set(self, section, option, value): """Write a value to the configuration file. If the file format is FORMAT_REPR, pass `value` through the `repr` function before writing it. Parameters ---------- section : str The section within the file which should contain the option to be written. option : str The key within `section` with which the data should be associated. value : (variant) The value to be associated with the given key in the given section. """ if self._fileFormat == FORMAT_REPR: value = repr(value) if not self._configParser.has_section(section): self._configParser.add_section(section) self._configParser.set(section, option, value) with open(self._filePath, 'w') as configFile: self._configParser.write(configFile) return value #-------------------------------------------------------------- Helper functions
def _bool(value): """Return the specified value as a boolean. Parameters ---------- value : (variant) The value which should be converted to a boolean. Returns ------- bool The `value`, cast to a boolean if possible, or `None` otherwise. """ if isinstance(value, str): value = value.strip() if value.lower() in _BOOLEAN_STATES: return _BOOLEAN_STATES[value.lower()] return None def _parseSingle(string): """Convert a single element into the appropriate type.""" string = string.strip() if len(string) == 0: return '' pattern = re.compile(r'[^0-9]') if not pattern.search(string): return int(string) pattern = re.compile(r'[^0-9\.eE]') if not pattern.search(string): if (string.count('.') <= 1 and (string.count('e') + string.count('E') <= 1)): return float(string) boolValue = _bool(string) if boolValue is not None: return boolValue if string[0] == string[-1]: if string[0] == '"' or string[0] == "'": return string[1:-1] elif string[1] == string[-1]: if ((string[0] == 'u' or string[0] == 'r') and (string[1] == '"' or string[1] == "'")): return string[2:-1] if string == 'None': return None return string def _parseSequence(string, delimiter=','): """Convert a string to a sequence of items.""" if not isinstance(string, str): return string string = string.strip() if string.startswith('[') and string.endswith(']'): sequenceType = 'list' elif string.startswith('(') and string.endswith(')'): sequenceType = 'tuple' else: return _parseSingle(string) string = string[1:-1] tokens = [] current = [] plev = 0 blev = 0 sqopen = False dqopen = False for char in string: if char == '[': blev += 1 current.append(char) elif char == ']': blev -= 1 current.append(char) elif char == '(': plev += 1 current.append(char) elif char == ')': plev -= 1 current.append(char) elif char == '"': dqopen = not dqopen current.append(char) elif char == "'": sqopen = not sqopen current.append(char) elif (char == delimiter and plev == 0 and blev == 0 and not sqopen and not dqopen): tokens.append(_parseSequence(''.join(current).strip())) current = [] else: current.append(char) if len(current) > 0: tokens.append(_parseSequence(''.join(current))) if sequenceType == 'tuple': tokens = tuple(tokens) return tokens if __name__ == '__main__': filename = '/home/thomas/Desktop/config.conf' conf = ConfigParser(filename, FORMAT_AUTO, defaultValues={('another_section', 'animal'): 7, ('cat', 'dog'): None}) print '********** Sections:' print repr(conf.getSections()) print repr(conf.get('section', 'option')) print repr(conf.get('section', 'list')) conf.set('section', 'option2', 9) conf.set('another_section', 'moose', 'animal') print repr(conf.get('another_section', 'moose')) print repr(conf.get('another_section', 'animal')) print repr(conf.get('cat', 'moose')) print repr(conf.get('cat', 'fish', 4)) print str(u'Thomas') print repr(u'Thomas')