from __future__ import annotations import json from pathlib import Path from typing import Dict, Optional, Union import yaml def clean_dict(dictionary: Dict) -> Optional[Dict]: aux = { k: clean_dict(v) for k, v in dictionary.items() if type(v) == dict } | { k: v for k, v in dictionary.items() if type(v) != dict } aux2 = { k: v for k, v in aux.items() if v is not None } return aux2 if aux2 != {} else None class Config: def merge_with(self, other: Config, strict: bool = False): """ Merges the other config into this one :param other: :param strict: whether conflicting options are allowed or not :return: self """ for var in vars(self): if not getattr(self, var): setattr(self, var, getattr(other, var)) else: if strict and getattr(other, var) is not None and getattr(self, var) != getattr(other, var): raise NotImplementedError return self @classmethod def from_json(cls, content: Union[Path, Dict]): if isinstance(content, Path): with open(content, 'r') as file: json_content: Dict = json.load(file) else: json_content = content config = cls() config.set_from_json(json_content) return config @classmethod def from_yaml(cls, content: Union[Path, str]): if isinstance(content, Path): with open(content, 'r') as file: json_content: Dict = yaml.safe_load(file) else: json_content = yaml.safe_load(content) return cls.from_json(json_content) def set_from_json(self, content: Optional[Dict]): raise NotImplementedError def to_json(self) -> Dict: raise NotImplementedError def dump_as_yaml(self, filename: Path, clean_none_entries: bool = True): with filename.open('w') as file: if clean_none_entries: simple_dict = clean_dict(self.to_json()) else: simple_dict = self.to_json() if simple_dict is not None: yaml.dump(simple_dict, file) else: pass # TODO def dump_as_json(self, filename: Path, clean_none_entries: bool = True): with open(filename, 'w') as config: if clean_none_entries: simple_dict = clean_dict(self.to_json()) else: simple_dict = self.to_json() if simple_dict is not None: json.dump(simple_dict, config) else: pass # TODO @classmethod def _fill_keys(cls, dictionary: Optional[Dict]): if dictionary is None: return cls().to_json() else: return recursive_merge_dictionaries( cls().to_json(), dictionary ) def recursive_merge_dictionaries(dict1: Dict, dict2: Dict) -> Dict: aux1 = { k: v for k, v in dict1.items() if type(v) == dict } aux2 = { k: v for k, v in dict2.items() if type(v) == dict } merged = { k: recursive_merge_dictionaries(v, aux2[k]) for k, v in aux1.items() if k in aux2.keys() } return dict1 | dict2 | merged