pytex/PyTeX/format/config.py

146 lines
4.5 KiB
Python
Raw Normal View History

2022-02-08 19:01:18 +01:00
from __future__ import annotations
2022-02-07 17:32:46 +01:00
import json
from pathlib import Path
from typing import Dict, Optional, Union, List
2022-02-07 17:32:46 +01:00
import yaml
def clean_list(list_: List) -> Optional[List]:
"""
Recursively removes all entries from list that are None,
empty dictionaries or empty lists.
Dicts ore lists that are None after cleaning will also be removed
Typically applied before dumping data to JSON where None values are default
and will be restored on read-in 'automatically'
:return:
:param list_: Any list
:return:
"""
ret = []
for elem in list_:
if type(elem) == list:
ret.append(clean_list(elem))
elif type(elem) == dict:
ret.append(clean_dict(elem))
elif elem is not None:
ret.append(elem)
return ret if ret != [] else None
2022-02-07 17:32:46 +01:00
def clean_dict(dictionary: Dict) -> Optional[Dict]:
"""
Recursively removes all entries from dictionary that are None,
empty dictionaries or empty lists.
Keys whose value is a dict / list that only contains non-valued keys will
then also be removed.
Typically applied before dumping data to JSON where None values are default
and will be restored on read-in 'automatically'
:param dictionary: Any dictionary
:return:
"""
aux: Dict = {
2022-02-07 17:32:46 +01:00
k: clean_dict(v) for k, v in dictionary.items() if type(v) == dict
} | {
k: clean_list(v)
for k, v in dictionary.items() if type(v) == list
} | {
k: v for k, v in dictionary.items() if type(v) != dict and type(v) != list
}
aux2: Dict = {
2022-02-07 17:32:46 +01:00
k: v for k, v in aux.items() if v is not None
}
return aux2 if aux2 != {} and aux2 != [] else None
2022-02-07 17:32:46 +01:00
class Config:
2022-02-08 19:01:18 +01:00
def merge_with(self, other: Config, strict: bool = False):
2022-02-07 17:32:46 +01:00
"""
2022-02-18 16:49:06 +01:00
Merges the other config into this one.
In conflicts, the called-on instance takes effect
2022-02-07 17:32:46 +01:00
: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):
2022-02-08 16:09:56 +01:00
with open(content, 'r') as file:
json_content: Dict = json.load(file)
else:
json_content = content
2022-02-07 17:32:46 +01:00
config = cls()
2022-02-08 16:09:56 +01:00
config.set_from_json(json_content)
2022-02-07 17:32:46 +01:00
return config
2022-02-08 16:34:59 +01:00
2022-02-07 17:32:46 +01:00
@classmethod
2022-02-08 16:34:59 +01:00
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)
2022-02-08 16:09:56 +01:00
return cls.from_json(json_content)
2022-02-07 17:32:46 +01:00
def set_from_json(self, content: Optional[Dict]):
2022-02-07 17:32:46 +01:00
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:
2022-02-09 01:22:25 +01:00
json.dump(simple_dict, config, indent=4)
2022-02-07 17:32:46 +01:00
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
)
2022-02-07 17:32:46 +01:00
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()
}
2022-02-07 18:36:30 +01:00
return dict1 | dict2 | merged