--- a +++ b/ReadersWriters/PyConfigParser.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python +# -*- coding: UTF-8 -*- +# +# Copyright 2017 University of Westminster. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +""" It is an interface for reading and writing configuration file using 'configParser'. +""" + +from typing import Dict, List +import sys +import configparser +import os +import logging + +__author__ = "Mohsen Mesgarpour" +__copyright__ = "Copyright 2016, https://github.com/mesgarpour" +__credits__ = ["Mohsen Mesgarpour"] +__license__ = "GPL" +__version__ = "1.1" +__maintainer__ = "Mohsen Mesgarpour" +__email__ = "mohsen.mesgarpour@gmail.com" +__status__ = "Release" + + +class PyConfigParser: + def __init__(self, + path: str, + app_name: str, + ext: str= ""): + """Initialise the objects and constants. + :param path: the file path of the configuration file. + :param app_name: the application name, which will be used as the log file name. + :param ext: the extension of the configuration file (default: 'ini'). + """ + self.__logger = logging.getLogger(app_name) + self.__logger.debug(__name__) + self.__path = os.path.abspath(path + "." + ext) + self.__config = None + self.refresh() + + def refresh(self): + """Refresh the configuration file reader and Reset the constants. + """ + self.__logger.debug("Refresh the Configuration file reader.") + try: + if not (os.path.exists(self.__path) and os.path.isfile(self.__path)): + self.__logger.warning(__name__ + " - Configuration file does not exist: \n" + self.__path) + self.reset() + else: + self.__config = configparser.ConfigParser() + self.__config.optionxform = str # make option case-sensitive + self.__config.read(self.__path) + except(): + self.__logger.error(__name__ + " - Error while opening a file: \n" + self.__path) + sys.exit() + + def reset(self): + """Reset the configuration file reader. + """ + self.__logger.debug("Reset the Configuration file.") + try: + open(self.__path, 'w').close() + except(): + self.__logger.error(__name__ + " - Could not create the config file: \n" + self.__path) + sys.exit() + + def sections(self) -> List: + """Get sections in the configuration file. + :return: section names. + """ + self.__logger.debug("Get Sections of the Configuration file.") + return self.__config.sections() + + def subsections(self, + section: str) -> List: + """Get sub-sections under the specified section name. + :param section: the section name. + :return: the sub-section names + """ + self.__logger.debug("Get Subsections of the Configuration file.") + return self.__config.items(section) + + def option(self, + section: str, + key: str) -> str: + """Get the option for the specified key and section. + :param section: a section in the configuration. + :param key: a key of a section in the configuration. + :return: the value (option) of the key. + """ + self.__logger.debug("Read an Option in the Configuration file.") + try: + value = self.__config.get(section, key) + except configparser.NoSectionError: + self.__logger.error(__name__ + " - Invalid Section: [Section: " + + str(section) + "; Key:" + str(key) + "]") + sys.exit() + except configparser.NoOptionError: + self.__logger.error(__name__ + " - Invalid Option: [Section: " + + str(section) + "; Key:" + str(key) + "]") + sys.exit() + except(): + self.__logger.error(__name__ + " - Invalid Configuration: [Section: " + + str(section) + "; Key:" + str(key) + "]") + sys.exit() + return value + + def options(self, + section: str, + keys: List=None) -> Dict: + """Get the options for all or the specified keys in the section. + :param section: a section in the configuration. + :param keys: keys of a section (option) in the configuration. + :return: the values (options) of the keys. + """ + self.__logger.debug("Read Section Options in the Configuration file.") + values = [] + try: + if keys is None: + values = dict(self.subsections(section)) + else: + for k in keys: + values.append(self.__config.get(section, k)) + except configparser.NoSectionError: + self.__logger.error(__name__ + " - Invalid Section: " + + "[Section: " + str(section) + "]") + sys.exit() + except(): + self.__logger.error(__name__ + " - Invalid Configuration.") + sys.exit() + return values + + def read_dict(self, + section: str=None) -> Dict: + """Read the configuration and save into dictionary. + :param section: the section name + :return: the read configuration file + """ + self.__logger.debug("Read into Dictionary.") + dic = dict() + sections = self.sections() + + if section is None: + for section in sections: + dic[section] = self.options(section) + for k, v in dic[section].items(): + dic[section][k] = str(v).split(',') + else: + if section in set(sections): + dic = self.options(section) + for k, v in dic.items(): + dic[k] = str(v).split(',') + return dic + + def write_dict(self, + dic: Dict, + section: str, + append: str=False): + """Write from the inputted dictionary into the configuration file. + :param dic: the inputted dictionary to write into the configuration file. + :param section: the section name. + :param append: indicates if the write appends to any existing configuration file. + """ + self.__logger.debug("Write from Dictionary.") + # set + config = configparser.RawConfigParser() + config.optionxform = str # make option is case-sensitive + config.add_section(section) + keys = list(dic.keys()) + + for key in keys: + if isinstance(dic[key], list): + config.set(section, key, ','.join(dic[key])) + else: + config.set(section, key, dic[key]) + + # write + if append is False: + with open(self.__path, 'w') as file: + config.write(file) + else: + with open(self.__path, 'a') as file: + config.write(file) + + def write_option(self, + section: str, + option: str, + value: str): + """Remove then add the specified option to the configuration file. + :param section: the section name. + :param option: the option name to be removed then added with new value. + :param value: the option value to be removed then added with new value. + """ + self.__logger.debug("Write an Option into the Configuration file.") + self.__remove_option(section, option) + self.__add_option(section, option, value) + + def __remove_option(self, + section: str, + option: str): + """Remove an option from the configuration file. + :param section: the section name. + :param option: the option name to remove. + """ + self.__logger.debug("Remove an Option from the Configuration file.") + section = "[" + section + "]" + option += "=" + match = False + + # read + with open(self.__path, 'r') as file: + lines = file.readlines() + + # delete + with open(self.__path, 'w') as file: + # remove + for line in lines: + if line.strip().startswith(section): + match = True + if match is True and line.replace(' ', '').startswith(option): + match = False + continue + file.write(line) + + def __add_option(self, + section: str, + option: str, + value: str): + """Add an option to the configuration file. + :param section: the section name. + :param option: the option name to be written. + :param value: the option value to be written. + """ + self.__logger.debug("Add an Option to the Configuration file.") + section = "[" + section + "]" + match = False + + # read + with open(self.__path, 'r') as file: + lines = file.readlines() + + # append + with open(self.__path, 'w') as file: + for line in lines: + if line.strip().startswith(section): + file.write(line) + file.write(option + " = " + ",".join(value) + "\n") + match = True + else: + file.write(line) + if match is False: + file.write("\n" + section + "\n" + option + " = " + ",".join(value) + "\n")