Source code for denspp.offline.yaml_handler
import yaml
from logging import getLogger, Logger
from typing import Any
from os import makedirs
from os.path import join, exists, isabs
from denspp.offline import get_path_to_project_start
[docs]
class YamlHandler:
__logger: Logger
_ending_chck: list = ['.yaml', '.yml']
_path2folder: str
_file_name: str
_template: Any
def __init__(self, template: Any | dict, path: str= 'config', file_name: str= 'Config_Train'):
"""Creating a class for handling YAML files
:param template: Dummy dataclass with entries or dictionary (is only generated if YAML not exist)
:param path: String with path to the folder which has the YAML file [Default: '']
:param file_name: String with name of the YAML file [Default: 'Config_Train']
"""
self.__logger = getLogger(__name__)
self._path2folder = join(get_path_to_project_start(), path) if not isabs(path) else path
self._file_name = self.__remove_ending_from_filename(file_name)
self._template = template
makedirs(self._path2folder, exist_ok=True)
if not exists(self.__path2chck):
data2yaml = template if isinstance(template, dict) else self.__translate_dataclass_to_dict(template)
self.write_dict_to_yaml(data2yaml)
self.__logger.info(f"Create new yaml file in folder: {self._path2folder}")
@property
def __path2chck(self) -> str:
"""Getting the path to the desired CSV file"""
return join(self._path2folder, f"{self._file_name}{self._ending_chck[0]}")
def __remove_ending_from_filename(self, file_name: str) -> str:
"""Function for removing data type ending
:param file_name: String with file name
:return:
String with file name without data type ending
"""
used_file_name = [file_name.split(file_end)[0] for file_end in self._ending_chck if file_end in file_name]
return used_file_name[0] if len(used_file_name) > 0 else file_name
@staticmethod
def __translate_dataclass_to_dict(class_content: type) -> dict:
"""Translating all class variables with default values into dict
:param class_content: Class content
:return: Dict with all variables
"""
return {key: value for key, value in class_content.__dict__.items()
if not key.startswith('__') and not callable(key)}
def __check_scheme_validation(self, template: type | dict, real_file: type | dict) -> bool:
"""Function for validating the key entries from template yaml and real yaml file
:param template: Dictionary or class from the template for generating yaml file
:param real_file: Dictionary from real_file
:return:
Boolean decision if both key are equal
"""
keys_tmplt = self.__translate_dataclass_to_dict(template).keys() if not isinstance(template, dict) else template.keys()
keys_real = self.__translate_dataclass_to_dict(real_file).keys() if not isinstance(real_file, dict) else real_file.keys()
equal_chck = keys_tmplt == keys_real
if not equal_chck:
list_not0 = [key for key in keys_real if key not in keys_tmplt]
list_not1 = [key for key in keys_tmplt if key not in [keys_real, list_not0]]
list_not0.extend(list_not1)
raise RuntimeError(f"Config file not valid (wrong keys: {list_not0})! - Please check and correct/remove actual config file!")
else:
return equal_chck
[docs]
def write_dict_to_yaml(self, config_data: dict, print_output: bool=False) -> None:
"""Writing list with configuration sets to YAML file
Args:
config_data: Dict. with configuration
print_output: Printing the data in YAML format
Returns:
None
"""
makedirs(self._path2folder, exist_ok=True)
with open(self.__path2chck, 'w') as f:
yaml.dump(config_data, f, sort_keys=False)
if print_output:
print(yaml.dump(config_data, sort_keys=False))
[docs]
def get_dict(self) -> dict:
"""Getting the dictionary with configuration sets from YAML file
:return: Dict. with configuration
"""
if not exists(self.__path2chck):
raise FileNotFoundError("YAML does not exists - Please create one!")
else:
# --- Reading YAML file
with open(self.__path2chck, 'r') as f:
data = yaml.safe_load(f)
self.__logger.debug(f"... read YAML file: {self.__path2chck}")
self.__check_scheme_validation(self._template, data)
return data
[docs]
def get_class(self, class_constructor: type):
"""Getting all key inputs from yaml dictionary to a class
:return: Settings in specified dataclass
"""
data = self.get_dict()
return class_constructor(**data)