"""Config file parser and config file generator. Configuration values will be read from a file called . A config file with default values can be generated by running this module as a script and using the --generate option. The config file is a standard INI file that contains [sections] and options. Variables that are between %(option)s are interpolated automatically by the ConfigParser (with the caveat that they have to be in the same section). Variables that are between ${section:option} will be interpolated with the value from the appropriate section (similar to the way ConfigParse from Python3 works). Example of a config file: E.g. [Section1] key_1 = value 1 key_2 = value 2 country = mexico [section2] name = John lastname = Doe fullname = %(name)s %(lastname)s country = ${Section1:country} To access config values from other modules follow this approach: import Config.config as CONF full_name = CONF.get('section2', 'fullname') """ from __future__ import print_function import argparse import configparser import os import re CONFIG_FILE = os.path.join(os.path.dirname(__file__), 'config.ini') CONFIG = configparser.SafeConfigParser() def get(section, option, raw=False, variables=None): """Wrapper function to mimic behavior of ConfigParse (Python3). Python3 uses a ConfigParse that is different than the Python2 ConfigParser, it has some additional functionality. One of the biggest differences is that with ConfigParse you can interpolate variables from one section into another section of the config file. This functionality is not included in the ConfigParse. Since it is very useful, this function wraps the original get function from ConfigParser so it includes the missing interpolation. :param section: the section where the option to be gotten is :param option: the option to look for in the config file :param raw: all the '%' interpolations are expanded in the return values, unless the raw argument is true. :param variables: the option is looked up in variables (if provided) :return: the specified value from the config file """ # ensure file readed is up to date always CONFIG.read(CONFIG_FILE) original_option = CONFIG.get(section, option, raw=raw, vars=variables) # find all matches that have the following pattern: ${something} matches = re.findall(r'\$\{(.*?)\}', original_option) new_value = original_option for match in matches: # interpolate matches found with ${something} with the referenced # option from the appropriate section pattern = '${' + match + '}' value = match.split(':') foreign_value = CONFIG.get(value[0], value[1]) new_value = new_value.replace(pattern, foreign_value) if original_option != new_value: CONFIG.set(section, option, new_value) # return the interpolated value return CONFIG.get(section, option, raw=raw, vars=variables) def getint(section, option): """Wrapper that returns the value as an integer. :param section: the section where the option to be gotten is :param option: the option to look for in the config file :return: the specified value from the config file """ return int(CONFIG.get(section, option)) def getfloat(section, option): """Wrapper that returns the value as a float. :param section: the section where the option to be gotten is :param option: the option to look for in the config file :return: the specified value from the config file """ return float(CONFIG.get(section, option)) def getboolean(section, option): """Wrapper that returns the value as a boolean. :param section: the section where the option to be gotten is :param option: the option to look for in the config file :return: the specified value from the config file """ value = CONFIG.get(section, option) return value.lower() == 'true' def _unload_current_values(): """Removes current sections from the existing config object""" for section in CONFIG.sections(): CONFIG.remove_section(section) def create_config(): """Creates the config file in the current directory""" if os.path.isfile(CONFIG_FILE): os.remove(CONFIG_FILE) with open(CONFIG_FILE, 'wb') as configfile: _unload_current_values() CONFIG.write(configfile) def parse_arguments(): """Parses arguments from the command line""" parser = argparse.ArgumentParser( description='Config file generator') parser.add_argument( '--generate', action='store_true', help='Generates a config file with default values') return parser.parse_args() if __name__ == '__main__': ARGUMENTS = parse_arguments() if ARGUMENTS.generate: create_config() print('Config file created at: {name}'.format(name=CONFIG_FILE))