import os from repo_cloner.lib.default_cloner_config import DefaultClonerConfig from repo_cloner.lib.config_file_not_found_error import ConfigFileNotFoundError import logging l = logging.getLogger("rc.cfg") class ClonerConfig(DefaultClonerConfig): def __init__(self): super().__init__() self.__properties = list(filter(self.has_property, dir(self))) self.__values = {} def __try_default(self, key: str, default_value): if not self.has_property(key): raise KeyError(f"{key} is not recognized config option") if key in self.__values: return self.__values[key] return default_value @property def cloner_project_name(self) -> str: return self.__try_default("cloner_project_name", super(ClonerConfig, self).cloner_project_name) @property def cloner_repo_url(self) -> str: return self.__try_default("cloner_repo_url", super(ClonerConfig, self).cloner_repo_url) @property def cloner_interval(self) -> int: return self.__try_default("cloner_interval", super(ClonerConfig, self).cloner_interval) @property def cloner_submodules(self) -> bool: return self.__try_default("cloner_submodules", super(ClonerConfig, self).cloner_submodules) @property def cloner_submodule_depth(self) -> int: return self.__try_default("cloner_submodule_depth", super(ClonerConfig, self).cloner_submodule_depth) def _set_property(self, key: str, value): if not self.has_property(key): raise KeyError(f"{key} is not recognized config option") requested_type = type(self.__getattribute__(key)); if not isinstance(value, requested_type): l.debug(f"Trying conversion of {key} from {type(value)} to {requested_type}") try: if requested_type == bool: value = True if value == "1" else False else: value = requested_type(value) l.debug(f"Conversion result: {type(value)}: {value}") except Exception as e: l.critical(f"Conversion failed! ({str(e)}") raise ValueError( f"Invalid value for key {key}: type is {type(value)} instead of {requested_type}, conversion failed") self.__values[key] = value class ClonerConfigParser: __file: str = "" __invalid_lines: list = [] def __init__(self, config_file: str): self.__file = config_file self.__config = ClonerConfig() self.__invalid_lines = [] l.info(f"Parsing cloner config file: {self.__file}") if not os.path.exists(self.__file): l.critical(f"Config file: {self.__file} does not exist!") raise ConfigFileNotFoundError(self.__file) # parse config file raw_lines = [] with open(self.__file, "r") as f: raw_lines = f.readlines() l.debug(f"Readen {len(raw_lines)} lines") lines = [] # strip lines, remove comments line: str = "" for line in raw_lines: if not line.startswith('#'): line = line.strip() # empty line? skip if len(line) > 0: lines.append(line) l.debug(f"Found {len(lines)} config lines") for line in lines: if not "=" in line: l.warning(f"Line '{line}' has invalid format!") self.__invalid_lines.append((line, None)) continue eq = line.find("=") key: str = line[0:eq].strip() val: str = line[eq + 1:].strip() l.debug(f"Found config pair: {key} => '{val}'") try: self.__config._set_property(key, val) except BaseException as e: self.__invalid_lines.append((key, val)) l.critical(str(e)) @property def config(self) -> ClonerConfig: return self.__config @property def invalid(self) -> bool: return len(self.__invalid_lines) > 0 @property def invalid_lines(self): return self.__invalid_lines