Ingen beskrivning
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

settings_loader.py 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #-*- coding: utf-8 -*-
  2. import configparser
  3. import os
  4. import glob
  5. import copy
  6. from lodel.context import LodelContext
  7. LodelContext.expose_modules(globals(), {
  8. 'lodel.logger': 'logger',
  9. 'lodel.settings.utils': ['SettingsError', 'SettingsErrors'],
  10. 'lodel.validator.validator': ['ValidationError']})
  11. ##@brief Merges and loads configuration files
  12. class SettingsLoader(object):
  13. ## To avoid the DEFAULT section whose values are found in all sections, we
  14. # have to give it an unsual name
  15. DEFAULT_SECTION = 'lodel2_default_passaway_tip'
  16. ## @brief Virtual filename when default value is used
  17. DEFAULT_FILENAME = 'default_value'
  18. ##@brief Constructor
  19. # @param conf_path str : conf.d path
  20. def __init__(self, conf_path):
  21. self.__conf_path = conf_path
  22. self.__conf_sv = dict()
  23. self.__conf = self.__merge()
  24. # Stores errors
  25. self.__errors_list = []
  26. ##@brief Lists and merges files in settings_loader.conf_path
  27. # @return dict()
  28. def __merge(self):
  29. conf = dict()
  30. l_dir = glob.glob(self.__conf_path+'/*.ini')
  31. logger.debug("SettingsLoader found those settings files : %s" % (
  32. ', '.join(l_dir)))
  33. for f_ini in l_dir:
  34. config = configparser.ConfigParser(default_section=self.DEFAULT_SECTION, interpolation=None)
  35. config.read(f_ini)
  36. for section in [s for s in config if s != self.DEFAULT_SECTION]:
  37. if section not in conf:
  38. conf[section] = dict()
  39. for param in config[section]:
  40. if param not in conf[section]:
  41. conf[section][param] = dict()
  42. conf[section][param]['value'] = config[section][param]
  43. conf[section][param]['file'] = f_ini
  44. self.__conf_sv[section + ':' + param] = f_ini
  45. else:
  46. raise SettingsError("Error redeclaration of key %s \
  47. in section %s. Found in %s and %s" % (\
  48. section, param, f_ini, conf[section][param]['file']))
  49. return conf
  50. ##@brief Returns option if exists default_value else and validates
  51. # @param section str : name of the section
  52. # @param keyname str
  53. # @param validator callable : takes one argument value and raises validation fail
  54. # @param default_value *
  55. # @param mandatory bool
  56. # @return the option
  57. def getoption(self, section, keyname, validator, default_value=None, mandatory=False):
  58. conf = self.__conf
  59. if section not in conf:
  60. conf[section] = dict()
  61. sec = conf[section]
  62. result = None
  63. if keyname in sec:
  64. result = sec[keyname]['value']
  65. if result is not None:
  66. result = result.strip()
  67. if len(result) == 0:
  68. result = None
  69. try:
  70. del self.__conf_sv[section + ':' + keyname]
  71. except KeyError: #allready fetched
  72. pass
  73. if result is None:
  74. if default_value is None and mandatory:
  75. msg = "Default value mandatory for option %s" % keyname
  76. expt = SettingsError(msg=msg, key_id=section+'.'+keyname, \
  77. filename=sec[keyname]['file'])
  78. self.__errors_list.append(expt)
  79. return
  80. else:
  81. sec[keyname] = dict()
  82. sec[keyname]['value'] = default_value
  83. sec[keyname]['file'] = SettingsLoader.DEFAULT_FILENAME
  84. result = default_value
  85. logger.debug("Using default value for configuration key %s:%s" \
  86. % (section, keyname))
  87. try:
  88. return validator(result)
  89. except Exception as e:
  90. # Generating nice exceptions
  91. if False and sec[keyname]['file'] == SettingsLoader.DEFAULT_FILENAME:
  92. expt = SettingsError(msg='Mandatory settings not found', \
  93. key_id=section+'.'+keyname)
  94. self.__errors_list.append(expt)
  95. else:
  96. expt = ValidationError("For %s.%s : %s" % (section, keyname, e))
  97. expt2 = SettingsError(msg=str(expt), \
  98. key_id=section+'.'+keyname, \
  99. filename=sec[keyname]['file'])
  100. self.__errors_list.append(expt2)
  101. return
  102. ##@brief Sets option in a config section. Writes in the conf file
  103. # @param section str : name of the section
  104. # @param keyname str
  105. # @param value str
  106. # @param validator callable : takes one argument value and raises validation fail
  107. # @return the option
  108. def setoption(self, section, keyname, value, validator):
  109. f_conf = copy.copy(self.__conf[section][keyname]['file'])
  110. if f_conf == SettingsLoader.DEFAULT_FILENAME:
  111. f_conf = self.__conf_path + '/generated.ini'
  112. conf = self.__conf
  113. conf[section][keyname] = value
  114. config = configparser.ConfigParser()
  115. config.read(f_conf)
  116. if section not in config:
  117. config[section] = {}
  118. config[section][keyname] = validator(value)
  119. with open(f_conf, 'w') as configfile:
  120. config.write(configfile)
  121. ##@brief Saves new partial configuration. Writes in the conf files corresponding
  122. # @param sections dict
  123. # @param validators dict of callable : takes one argument value and raises validation fail
  124. def saveconf(self, sections, validators):
  125. for sec in sections:
  126. for kname in sections[sec]:
  127. self.setoption(sec, kname, sections[sec][kname], validators[sec][kname])
  128. ##@brief Returns the section to be configured
  129. # @param section_prefix str
  130. # @param default_section str
  131. # @return the section as dict()
  132. def getsection(self, section_prefix, default_section=None):
  133. conf = copy.copy(self.__conf)
  134. sections = []
  135. if section_prefix in conf:
  136. sections.append(section_prefix)
  137. for sect_names in conf:
  138. if sect_names in sections:
  139. pass
  140. elif sect_names.startswith(section_prefix + '.'):
  141. sections.append(sect_names)
  142. if sections == [] and default_section:
  143. sections.append(section_prefix + '.' + default_section)
  144. elif sections == []:
  145. raise NameError("Not existing settings section : %s" % section_prefix)
  146. return sections
  147. ##@brief Returns invalid settings
  148. #
  149. # This method returns all the settings that was not fecthed by
  150. # getsection() method. For the Settings object it allows to know
  151. # the list of invalids settings keys
  152. # @return a dict with SECTION_NAME+":"+KEY_NAME as key and the filename
  153. # where the settings was found as value
  154. def getremains(self):
  155. return self.__conf_sv
  156. ##@brief Raise a SettingsErrors exception if some confs remains
  157. #@note typically used at the end of Settings bootstrap
  158. def raise_errors(self):
  159. remains = self.getremains()
  160. err_l = self.__errors_list
  161. for key_id, filename in remains.items():
  162. err_l.append(SettingsError(msg="Invalid configuration key", \
  163. key_id=key_id, \
  164. filename =filename))
  165. if len(err_l) > 0:
  166. raise SettingsErrors(err_l)
  167. else:
  168. return