import appdirs from . import _util import argparse import configobj import os import sys import validate from ._bootstrap import _bootstrap_logger from . import _util class Config: """ Structure to store application runtime configuration. Also contains functionality to load configuration from local site file. """ def __init__(self, configspec_filepath=None): self.config_obj = None self._config_filepaths = [] self._configspec_filepath = None self.configspec_filepath = configspec_filepath def __delitem__(self, key): """ Deletes the configuration item identified by in the internal configuration storage. """ try: del self[key] except KeyError as ex: pass def __getitem__(self, key): """ Returns the vaLue of the configuration item identified by . """ try: return self.config_obj[key].dict() except KeyError as ex: # raise ConfigurationItemNotFoundError() raise def __setitem__(self, key, value): """ Assigns the value of the configuration item identified by as . """ self[key] = value @property def config_filepath(self, idx=0): """ Returns the config filepath (optionally specified by index when using multiple config files). """ assert idx>=0, 'invalid idx argument: index must be greater than 0' if len(self._config_filepaths) > 0: try: return self._config_filepaths[idx] except ValueError as ex: return @config_filepath.setter def config_filepath(self, value, idx=0): """ Assigns as the config filepath (optionally specified by index when using multiple config files). """ assert idx>=0, 'invalid idx argument: index must be greater than 0' self._config_filepaths[idx] = value @property def configspec_filepath(self): return self._configspec_filepath @configspec_filepath.setter def configspec_filepath(self, filepath): if _util.does_file_exist(filepath): self._configspec_filepath = filepath else: _bootstrap_logger.error( 'failed to set config.spec: file not found ' '(%s)', filepath) def load_config_from_file(self, config_filepath): """ Loads configuration settings from file, overwritting all configuration. """ # Record all config.ini files passed in if config_filepath not in self._config_filepaths: self._config_filepaths.append(config_filepath) # Check for config.spec if self.configspec_filepath: _bootstrap_logger.info('using config.spec: %s', self.configspec_filepath) else: _bootstrap_logger.info('config.spec not defined') _bootstrap_logger.info('using config file: %s', config_filepath) # Pre-check for config.ini existence if _util.does_file_exist(config_filepath): _bootstrap_logger.info('existing config file found') else: _bootstrap_logger.info('no config file found: using defaults') # interpolation='template' changes config file variable replacement to # use the form $var instead of %(var)s, which is useful to enable # literal %(text)s values in the config. try: configspec_filepath = self.configspec_filepath if configspec_filepath: self.config_obj = configobj.ConfigObj( config_filepath, configspec=configspec_filepath, interpolation='template' ) else: self.config_obj = configobj.ConfigObj( config_filepath, # configspec=configspec_filepath, interpolation='template' ) except configobj.ParseError as ex: msg = 'failed to load config: error in config.spec configuration: {}'.format(config_filepath) _bootstrap_logger.error(msg) _util.eprint(msg) return False except OSError as ex: msg = 'failed to load config: config.spec file not found' _bootstrap_logger.error(msg) _util.eprint(msg) return False # Hack the configobj module to alter the interpolation for validate.py: configobj.DEFAULT_INTERPOLATION = 'template' self.config_obj.filename = config_filepath if self.configspec_filepath: # Validate config.ini against config.spec try: _bootstrap_logger.info('validating config file against spec') val = validate.Validator() test = self.config_obj.validate(val, copy=True) if test is not True: _bootstrap_logger.critical('config file failed validation') _bootstrap_logger.critical('config file errors: %s', test) return False except ValueError as ex: _bootstrap_logger.error('failed validating configspec') return False # Create the config file if it doesn't exist # if not _util.does_file_exist(config_filepath): if True: _bootstrap_logger.info('writing new config file: %s', config_filepath) dirname = os.path.dirname(config_filepath) _util.ensure_dir_exists(dirname) self.config_obj.write() _bootstrap_logger.info('done loading config file') return True def print_config(self): """ Print configuration to stdout. """ print('config:') self.config_obj.walk(print) for section in self.config_obj.sections: print(section) for key in self.config_obj[section]: print(' ', self.config_obj[section][key]) class EnvironmentVariables: def __init__(self): raise NotImplementedError class ConfigurationItemNotFoundError(Exception): pass