diff --git a/src/music_kraken/utils/config/base_classes.py b/src/music_kraken/utils/config/base_classes.py index 81521ae..c462922 100644 --- a/src/music_kraken/utils/config/base_classes.py +++ b/src/music_kraken/utils/config/base_classes.py @@ -2,6 +2,8 @@ import logging from dataclasses import dataclass from typing import Optional, List, Union, Dict +from ..exception.config import SettingNotFound, SettingValueError + COMMENT_PREFIX = "#" @@ -28,16 +30,25 @@ class Attribute: description: Optional[str] value: Union[str, List[str]] - rule: str = "The setting {name} can't be {value}." + def validate(self, value: str): + """ + This function validates a new value without setting it. + + :raise SettingValueError: + :param value: + :return: + """ + pass def set_value(self, value: str): - _value = self.value + """ + :raise SettingValueError: if the value is invalid for this setting + :param value: + :return: + """ + self.validate(value) self.value = value - try: - _ = self.object_from_value - except ValueError: - raise ValueError(self.rule.format(name=self.name, value=self.value)) @property def description_as_comment(self): @@ -62,34 +73,78 @@ class StringAttribute(SingleAttribute): class IntAttribute(SingleAttribute): - rule = "The setting {name} has to be an integer, not {value}" + def validate(self, value: str): + if not value.isdigit(): + raise SettingValueError( + setting_name=self.name, + setting_value=value, + rule="has to be a digit (an int)" + ) @property def object_from_value(self) -> int: if not self.value.isdigit(): - raise ValueError(f"The value of {self.name} needs to be an integer, not {self.value}") - - return int(self.value) + return int(self.value) class FloatAttribute(SingleAttribute): - rule = "The setting {name} has to be a number, not {value}" + def validate(self, value: str): + if not value.isnumeric(): + raise SettingValueError( + setting_name=self.name, + setting_value=value, + rule="has to be numeric (an int or float)" + ) @property def object_from_value(self) -> float: - if not self.value.isnumeric(): - raise ValueError(f"The value of {self.name} needs to be a number, not {self.value}") - - return float(self.value) + if self.value.isnumeric(): + return float(self.value) class ListAttribute(Attribute): value: List[str] + has_default_values: bool = True + + def set_value(self, value: str): + """ + Due to lists being represented as multiple lines with the same key, + this appends, rather than setting anything. + + :raise SettingValueError: + :param value: + :return: + """ + self.validate(value) + + # resetting the list to an empty list, if this is the first config line to load + if self.has_default_values: + self.value = [] + + self.value.append(value) + def __str__(self): return f"{self.description_as_comment}\n" + \ "\n".join(f"{self.name}={element}" for element in self.value) + def single_object_from_element(self, value: str): + return value + + @property + def object_from_value(self) -> list: + """ + THIS IS NOT THE PROPERTY TO OVERRIDE WHEN INHERETING ListAttribute + + :return: + """ + + parsed = list() + for raw in self.value: + parsed.append(self.single_object_from_element(raw)) + + return parsed + @dataclass class Description: @@ -131,9 +186,17 @@ class Section: self.name_attribute_map[element.name] = element - def __setitem__(self, key, value): - if key not in self.name_attribute_map: - raise KeyError(f"There is no such setting with the name: {key}") + def modify_setting(self, setting_name: str, new_value: str): + """ + :raise SettingValueError, SettingNotFound: + :param setting_name: + :param new_value: + :return: + """ - attribute = self.name_attribute_map[key] - attribute.set_value(value) + if setting_name not in self.name_attribute_map: + raise SettingNotFound( + setting_name=setting_name + ) + + self.name_attribute_map[setting_name].set_value(new_value) diff --git a/src/music_kraken/utils/config/connection.py b/src/music_kraken/utils/config/connection.py new file mode 100644 index 0000000..e69de29 diff --git a/src/music_kraken/utils/config/misc.py b/src/music_kraken/utils/config/misc.py new file mode 100644 index 0000000..e69de29 diff --git a/src/music_kraken/utils/exception/__init__.py b/src/music_kraken/utils/exception/__init__.py new file mode 100644 index 0000000..4e1f95f --- /dev/null +++ b/src/music_kraken/utils/exception/__init__.py @@ -0,0 +1 @@ +__all__ = ["config"] diff --git a/src/music_kraken/utils/exception/config.py b/src/music_kraken/utils/exception/config.py new file mode 100644 index 0000000..845bec7 --- /dev/null +++ b/src/music_kraken/utils/exception/config.py @@ -0,0 +1,25 @@ +class SettingException(Exception): + pass + + +class SettingNotFound(SettingException): + def __init__(self, setting_name: str): + self.setting_name = setting_name + + +class SettingValueError(SettingException): + def __init__(self, setting_name: str, setting_value: str, rule: str): + """ + The rule has to be such, that the following format makes sense: + {name} {rule}, not '{value}' + + :param setting_name: + :param setting_value: + :param rule: + """ + self.setting_name = setting_name + self.setting_value = setting_value + self.rule = rule + + def __str__(self): + return f"{self.setting_name} {self.rule}, not '{self.setting_value}'"