123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- #-*- coding: utf-8 -*-
- import configparser
- import os
- import glob
- import copy
-
- from lodel.context import LodelContext
-
- LodelContext.expose_modules(globals(), {
- 'lodel.logger': 'logger',
- 'lodel.settings.utils': ['SettingsError', 'SettingsErrors'],
- 'lodel.validator.validator': ['ValidationError']})
-
- ##@brief Merges and loads configuration files
- class SettingsLoader(object):
-
- ## To avoid the DEFAULT section whose values are found in all sections, we
- # have to give it an unsual name
- DEFAULT_SECTION = 'lodel2_default_passaway_tip'
-
- ## @brief Virtual filename when default value is used
- DEFAULT_FILENAME = 'default_value'
-
- ##@brief Constructor
- # @param conf_path str : conf.d path
- def __init__(self, conf_path):
- self.__conf_path = conf_path
- self.__conf_sv = dict()
- self.__conf = self.__merge()
- # Stores errors
- self.__errors_list = []
-
- ##@brief Lists and merges files in settings_loader.conf_path
- # @return dict()
- def __merge(self):
- conf = dict()
- l_dir = glob.glob(self.__conf_path+'/*.ini')
- logger.debug("SettingsLoader found those settings files : %s" % (
- ', '.join(l_dir)))
-
- for f_ini in l_dir:
- config = configparser.ConfigParser(default_section=self.DEFAULT_SECTION, interpolation=None)
- config.read(f_ini)
- for section in [s for s in config if s != self.DEFAULT_SECTION]:
- if section not in conf:
- conf[section] = dict()
- for param in config[section]:
- if param not in conf[section]:
- conf[section][param] = dict()
- conf[section][param]['value'] = config[section][param]
- conf[section][param]['file'] = f_ini
- self.__conf_sv[section + ':' + param] = f_ini
- else:
- raise SettingsError("Error redeclaration of key %s \
- in section %s. Found in %s and %s" % (\
- section, param, f_ini, conf[section][param]['file']))
- return conf
-
- ##@brief Returns option if exists default_value else and validates
- # @param section str : name of the section
- # @param keyname str
- # @param validator callable : takes one argument value and raises validation fail
- # @param default_value *
- # @param mandatory bool
- # @return the option
- def getoption(self, section, keyname, validator, default_value=None, mandatory=False):
- conf = self.__conf
- if section not in conf:
- conf[section] = dict()
-
- sec = conf[section]
- result = None
- if keyname in sec:
- result = sec[keyname]['value']
- if result is not None:
- result = result.strip()
- if len(result) == 0:
- result = None
- try:
- del self.__conf_sv[section + ':' + keyname]
- except KeyError: #allready fetched
- pass
- if result is None:
- if default_value is None and mandatory:
- msg = "Default value mandatory for option %s" % keyname
- expt = SettingsError(msg=msg, key_id=section+'.'+keyname, \
- filename=sec[keyname]['file'])
- self.__errors_list.append(expt)
- return
- else:
- sec[keyname] = dict()
- sec[keyname]['value'] = default_value
- sec[keyname]['file'] = SettingsLoader.DEFAULT_FILENAME
- result = default_value
- logger.debug("Using default value for configuration key %s:%s" \
- % (section, keyname))
-
- try:
- return validator(result)
- except Exception as e:
- # Generating nice exceptions
- if False and sec[keyname]['file'] == SettingsLoader.DEFAULT_FILENAME:
- expt = SettingsError(msg='Mandatory settings not found', \
- key_id=section+'.'+keyname)
- self.__errors_list.append(expt)
- else:
- expt = ValidationError("For %s.%s : %s" % (section, keyname, e))
- expt2 = SettingsError(msg=str(expt), \
- key_id=section+'.'+keyname, \
- filename=sec[keyname]['file'])
- self.__errors_list.append(expt2)
- return
-
- ##@brief Sets option in a config section. Writes in the conf file
- # @param section str : name of the section
- # @param keyname str
- # @param value str
- # @param validator callable : takes one argument value and raises validation fail
- # @return the option
- def setoption(self, section, keyname, value, validator):
- f_conf = copy.copy(self.__conf[section][keyname]['file'])
- if f_conf == SettingsLoader.DEFAULT_FILENAME:
- f_conf = self.__conf_path + '/generated.ini'
-
- conf = self.__conf
- conf[section][keyname] = value
- config = configparser.ConfigParser()
- config.read(f_conf)
- if section not in config:
- config[section] = {}
- config[section][keyname] = validator(value)
-
- with open(f_conf, 'w') as configfile:
- config.write(configfile)
-
- ##@brief Saves new partial configuration. Writes in the conf files corresponding
- # @param sections dict
- # @param validators dict of callable : takes one argument value and raises validation fail
- def saveconf(self, sections, validators):
- for sec in sections:
- for kname in sections[sec]:
- self.setoption(sec, kname, sections[sec][kname], validators[sec][kname])
-
- ##@brief Returns the section to be configured
- # @param section_prefix str
- # @param default_section str
- # @return the section as dict()
- def getsection(self, section_prefix, default_section=None):
- conf = copy.copy(self.__conf)
-
- sections = []
- if section_prefix in conf:
- sections.append(section_prefix)
- for sect_names in conf:
- if sect_names in sections:
- pass
- elif sect_names.startswith(section_prefix + '.'):
- sections.append(sect_names)
- if sections == [] and default_section:
- sections.append(section_prefix + '.' + default_section)
- elif sections == []:
- raise NameError("Not existing settings section : %s" % section_prefix)
-
- return sections
-
- ##@brief Returns invalid settings
- #
- # This method returns all the settings that was not fecthed by
- # getsection() method. For the Settings object it allows to know
- # the list of invalids settings keys
- # @return a dict with SECTION_NAME+":"+KEY_NAME as key and the filename
- # where the settings was found as value
- def getremains(self):
- return self.__conf_sv
-
- ##@brief Raise a SettingsErrors exception if some confs remains
- #@note typically used at the end of Settings bootstrap
- def raise_errors(self):
- remains = self.getremains()
- err_l = self.__errors_list
- for key_id, filename in remains.items():
- err_l.append(SettingsError(msg="Invalid configuration key", \
- key_id=key_id, \
- filename =filename))
- if len(err_l) > 0:
- raise SettingsErrors(err_l)
- else:
- return
|