Source code for musicdb.lib.cfg.config

# MusicDB,  a music manager with web-bases UI that focus on music.
# Copyright (C) 2017 - 2021  Ralf Stemmer <ralf.stemmer@gmx.net>
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program 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 General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
This module handles the reading and writing of ini-files.
The definition of names is shown in the ini file example below.

    .. code-block:: ini

        [section]
        option = value
"""

import configparser
import os

[docs]class Config(configparser.ConfigParser, object): def __init__(self, filename=None): """ If a file name is given, the file gets read. """ super(Config, self).__init__() self.__filename = None self.__allownonevalue = False if filename: self.Load(filename)
[docs] def AllowNoneValue(self, allow=True): """ This method enables the possibility to configure (read) a ``None`` value. In case a value has the string ``"None"``, the :meth:`~musicdb.lib.cfg.config.Config.Get` method returns ``None``. The upper case N is important! Default behavior of this class is to return the string ``"None"`` as regular string. Args: allow (bool): if ``True`` none values are allowed, otherwise the string ``"None"`` gets returned as a string Returns: ``None`` Example: .. code-block:: ini [test] option = None .. code-block:: python cfg = Config("test.ini") x = cfg.Get(str, "test", "option") print(type(x)) # str cfg.AllowNoneValue() x = cfg.Get(str, "test", "option") print(type(x)) # NoneType """ self.__allownonevalue = allow return None
[docs] def Load(self, path=None): """ This method loads a configuration file. If the *path* parameter is not ``None``, the path given while instantiating the class will be overwritten. If the file was already loaded, it gets reloaded. Args: path (str): Absolute path to an ini file Returns: *Nothing* Raises: AssertionError: If internal path variable and parameter is ``None``. (No path given) """ if path == None and self.__filename == None: raise AssertionError("No filename was given to load") if path: self.__filename = path self.read(self.__filename)
[docs] def Reload(self): """ This method just calls :meth:`~musicdb.lib.cfg.config.Config.Load` without a path argument. """ self.Load()
[docs] def Save(self): """ This method saves all set values back to the configuration file. .. attention: Calling this method removes all comments from the file. The used library recreates and reformates the whole file. Changing configuration can be prevented by setting the file attributes to read only. If writing fails not because of missing write permission, an exception gets raised. """ try: with open(self.__filename, "w") as configfile: self.write(configfile) except IOError as e: if e[0] != 13: raise e # The user shall be able to forbid me messing up his config :D
# sync may be too aggressive. # If issue #65 (Syncing genre selection fails) is not solved without it, I add this later to the code. # To make sure it is actually propagated to the file system ``os.sync`` is executed after saving. #os.sync()
[docs] def OptionAvailable(self, section, option): """ This method can be used to check if an option exists in the configuration file. Args: section (str): Name of the section option (str): Name of the option Returns: ``True`` if the option exists, otherwise ``False`` """ if self.has_option(section, option): return True else: return False
# todo: auf allownonevalue eingegeh und dass None auch als default einfließen kann
[docs] def Get(self, datatype, section, option, default=None, islist=False): """ This method reads a value from the configuration. The values in the file are all stored as string. Using the *datatype* parameter will convert that string into a python data type. The default parameter can be used to define a value that shall be returned in case the option does not exist. This value can be of a different type than defined by the datatype parameter. If the value of an option is ``"None"``, and none type is allowed by calling :meth:`~musicdb.lib.cfg.config.Config.AllowNoneValue`, ``None`` gets returned. If the *islist* parameter is set to ``True``, the read value gets interpreted as comma separated list and split. A list of the striped values (without leading or trailing spaces) gets returned casted to the data type given by the *datatype* parameter. Args: datatype: The data type of the value. This can be ``bool``, ``int``, ``float`` or ``str``. section (str): Name of the section to read from option (str): Name of the option to read default: Default value in case the option does not exist islist (bool): Interpret the value as a comma separated list Returns: The value of an option Example: .. code-block:: ini [test] integer = 1000 list = 13, 23, 42 .. code-block:: python cfg = Config("test.ini") x = cfg.Get(int, "test", "integer") print(x) # 1000 x = cfg.Get(int, "test", "list", [], True) print(x) # [13, 23, 42] x = cfg.Get(int, "test", "noneexisting", 1337) print(x) # 1337 """ if not self.has_option(section, option): return default try: if self.__allownonevalue == True: entry = self.get(section, option) if entry == "None": return None if islist == True: entries = self.get(section, option) values = [datatype(x.strip()) for x in entries.split(",")] return values #value = datatype(self.get(section, option)) if datatype == bool: value = self.getboolean(section, option) elif datatype == int: value = self.getint(section, option) elif datatype == str: value = self.get(section, option) elif datatype == float: value = self.getfloat(section, option) except ValueError: return default return value
[docs] def Set(self, section, option, value): """ This method sets an option. The value gets casted to a string. If the section or option does not exist, it will be created. After setting the option, the :meth:`~musicdb.lib.cfg.config.Config.Save` method gets called to store the changes. This is necessary because there is no reliable destructor in python to. Args: section (str): Section to write at option (str): Name of the option to set value: Value to store Returns: *Nothing* """ if not self.has_section(section): self.add_section(section) self.set(section, option, str(value)) try: secobj = getattr(self, section) setattr(secobj, option, value) except Exception as e: pass self.Save() # save after every change… necessary because there are no reliable destructors in python
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4