import yaml class PathError(Exception): def __init__(self, path): self.path = path super().__init__(self.path) class PathNotFoundError(PathError): pass class MissingRequirementError(PathError): pass class NotDictError(PathError): pass class Config: def __init__(self, data, delimiter = "/", defaults = {}, requirements = [], **misc_options): self.data = yaml.safe_load(open(data)) if type(data) != dict else data self.delimiter = delimiter self.strict_subscript = misc_options.get("strict_subscript", False) self.clobber_subscript = misc_options.get("clobber_subscript", False) for r in requirements: if not self.get(r): raise MissingRequirementError(r) for d in defaults: if not self.get(d): self.set(d, defaults[d]) def split_path(self, path): return path.split(self.delimiter) def get(self, path, fallback = None, strict = False): elementsAll = self.split_path(path) def g(elems = elementsAll, obj = self.data): if not elems or not elems[0] or type(obj) != dict: return obj head, tail = elems[0], elems[1:] if strict and head not in obj: raise PathNotFoundError(path) return g(tail, obj.get(head, fallback)) return g() def set(self, path, value, clobber = False): elementsAll = self.split_path(path) def s(elems = elementsAll, obj = self.data): head, tail = elems[0], elems[1:] # YANDEV MODE ACTIVATE!!! if not tail: obj[head] = value else: if head not in obj or type(obj[head]) != dict: if clobber: obj[head] = {} else: raise NotDictError(self.delimiter.join(elementsAll[:-len(tail)])) s(tail, obj[head]) s() def __getitem__(self, path): return self.get(path, self.strict_subscript) def __setitem__(self, path, value): self.set(path, value, self.clobber_subscript)