Source code for x2gobroker.config

# -*- coding: utf-8 -*-
# vim:fenc=utf-8

# Copyright (C) 2010-2020 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
#
# X2Go Session Broker is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# X2Go Session Broker is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#
# This code was initially written by for the Python X2Go project:
#       2010 Dick Kniep <dick.kniep@lindix.nl>
#

"""\
X2GoConfig - helper class for parsing files in INI file format.

"""
__NAME__ = 'x2goinifiles-pylib'

# modules
import os
import configparser
import io

# Python X2GoBroker modules
import x2gobroker.utils

from x2gobroker.defaults import X2GOBROKER_HOME as _X2GOBROKER_HOME

[docs]class X2GoBrokerConfigFile(object): """ Class for processing an INI-file-like configuration file. If entries are omitted in such a config file, they are filled with default values (as hard-coded in Python X2GoBroker), so the resulting objects always contain the same fields. The default values are also used to define a data type for each configuration option. An on-the-fly type conversion takes place when loading the configuration file. """ defaultValues = { 'DEFAULT': { 'none': 'empty', }, } write_user_config = False user_config_file = None def __init__(self, config_files=[], defaults={}): """\ :param config_files: a list of configuration file names (e.g. a global filename and a user's home directory filename) :type config_files: ``list`` :param defaults: a cascaded Python dicitionary structure with INI file defaults (to override Python X2GoBroker's hard-coded defaults in L{defaults} :type defaults: ``dict`` """ # allow string/unicode objects as config_files, as well if type(config_files) == str: config_files = [config_files] self.config_files = config_files if x2gobroker.utils._checkConfigFileDefaults(defaults): self.defaultValues = defaults # we purposefully do not inherit the RawConfigParser class # here as we do not want to run into name conflicts between # X2GoBroker config file options and method / property names in # RawConfigParser... This is a pre-cautious approach... self.iniConfig = configparser.RawConfigParser(self.defaultValues) self.iniConfig.optionxform = str _create_file = False for file_name in self.config_files: if file_name.startswith(_X2GOBROKER_HOME): if not os.path.exists(file_name): x2gobroker.utils.touch_file(file_name) _create_file = True break self.load() if _create_file: self.write_user_config = True self.write() def __repr__(self): result = 'X2GoConfigFile(' for p in dir(self): if '__' in p or not p in self.__dict__: continue result += p + '=' + str(self.__dict__[p]) + ',' result = result.strip(',') return result + ')'
[docs] def load(self): """\ R(e-r)ead configuration file(s). """ _found_config_files = self.iniConfig.read(self.config_files) for file_name in _found_config_files: if file_name.startswith(os.path.normpath(_X2GOBROKER_HOME)): # we will use the first file found in the user's home dir for writing modifications self.user_config_file = file_name break self.config_files = _found_config_files self._fill_defaults()
def _storeValue(self, section, key, value): """\ Stores a value for a given section and key. This methods affects a :class:`configparser.RawConfigParser` object held in RAM. No configuration file is affected by this method. To write the configuration to disk use the L{write()} method. :param section: the INI file section :type section: ``str`` :param key: the INI file key in the given section :type key: ``str`` :param value: the value for the given section and key :type value: ``str``, ``list``, ``bool``, ... """ if type(value) is bool: self.iniConfig.set(section, key, str(int(value))) elif type(value) in (list, tuple): self.iniConfig.set(section, key, ", ".join(value)) elif type(value) is int: self.iniConfig.set(section, key, str(value)) else: self.iniConfig.set(section, key, value) def _fill_defaults(self): """\ Fills a :class:`configparser.RawConfigParser` object with the default config file values as pre-defined in Python X2GoBroker or. This RawConfigParser object is held in RAM. No configuration file is affected by this method. """ for section, sectiondict in list(self.defaultValues.items()): if section != 'DEFAULT' and not self.iniConfig.has_section(section): self.iniConfig.add_section(section) for key, value in list(sectiondict.items()): if self.iniConfig.has_option(section, key): continue self._storeValue(section, key, value)
[docs] def update_value(self, section, key, value): """\ Change a value for a given section and key. This method does not have any effect on configuration files. :param section: the INI file section :type section: ``str`` :param key: the INI file key in the given section :type key: ``str`` :param value: the value for the given section and key :type value: ``str``, ``list``, ``bool``, ... """ if not self.iniConfig.has_section(section): self.iniConfig.add_section(section) self._storeValue(section, key, value) self.write_user_config = True
[docs] def write(self): """\ Write the INI file modifications (RawConfigParser object) from RAM to disk. For writing the first of the ``config_files`` specified on instance construction that is writable will be used. """ if self.user_config_file and self.write_user_config: fd = open(self.user_config_file, 'w') self.iniConfig.write(fd) fd.close() self.write_user_config = False
[docs] def get_type(self, section, key): """\ Retrieve a value type for a given section and key. The returned value type is based on the default values dictionary. :param section: the INI file section :type section: ``str`` :param key: the INI file key in the given section :type key: ``str`` :returns: a Python variable type :rtype: class """ if section in list(self.defaultValues.keys()) and key in list(self.defaultValues[section].keys()): return type(self.defaultValues[section][key]) else: try: return type(self.defaultValues['DEFAULT'][key]) except KeyError: return type('')
[docs] def has_value(self, section, key): """\ Test if a given ``key`` in ``section`` exists (and has some sort of a value). :param section: the INI file section :type section: ``str`` :param key: the INI file key in the given section :type key: ``str`` :returns: return ``True`` if <key> in <section> exists :rtype: ``bool`` """ if section in self.iniConfig.sections(): return ( key in self.iniConfig.options(section) ) return False
[docs] def get_value(self, section, key, key_type=None): """\ Retrieve a value for a given section and key. :param section: the INI file section :type section: ``str`` :param key: the INI file key in the given section :type key: ``str`` :returns: the value for the given section and key :rtype: class """ if key_type is None: key_type = self.get_type(section, key) if self.iniConfig.has_option(section, key) or section == 'DEFAULT': if key_type is None: return self.iniConfig.get(section, key) if key_type is bool: return self.iniConfig.getboolean(section, key) elif key_type is int: try: return self.iniConfig.getint(section, key) except ValueError: _val = self.iniConfig.get(section, key) if _val != "not-set": raise else: return _val elif key_type is list: _val = self.iniConfig.get(section, key) _val = _val.strip() if _val.startswith('[') and _val.endswith(']'): return eval(_val) elif ',' in _val: _val = [ v.strip() for v in _val.split(',') ] else: _val = [ _val ] return _val else: _val = self.iniConfig.get(section, key) return _val
get = get_value __call__ = get_value
[docs] def get_defaults(self): """\ Get all keys and values from the [DEFAULT] section of the configuration file. :returns: the defaults with all keys and values :rtype: ``dict`` """ _my_defaults = {} _ini_defaults = self.iniConfig.defaults() for option in list(_ini_defaults.keys()): try: _my_defaults[option] = self.get('DEFAULT', option, key_type=self.get_type('DEFAULT', option)) except KeyError: continue try: del _my_defaults['default'] except KeyError: pass return _my_defaults
[docs] def get_section(self, section): """\ Get all keys and values for a certain section of the config file. :param section: the name of the section to get :type section: ``str`` :returns: the section with all keys and values :rtype: ``dict`` """ _section_config = {} for option in self.iniConfig.options(section): if option not in self.iniConfig.sections(): _section_config[option] = self.get(section, option, key_type=self.get_type(section, option)) return _section_config
[docs] def list_sections(self): """\ Return a list of all present sections in a config file. :returns: list of sections in this config file :rtype: ``list`` """ return [ s for s in self.iniConfig.sections() ]
@property def printable_config_file(self): """\ Returns a printable configuration file as a multi-line string. """ stdout = io.StringIO() self.iniConfig.write(stdout) _ret_val = stdout.getvalue() stdout.close() return _ret_val