class ContextualDataReference(metaclass=abc.ABCMeta): ''' Base class for autoloaded Contextual data references in Arjuna. Arguments: path: Path of the contextual data reference file. Note: It behaves like a Python dictionary. So, you get items by names/keys and loop over it. ''' def __init__(self, path): self.__path = path self.__name = _get_file_name(path) self.__map = CIStringDict() self._populate() self.__iter = None def __iter__(self): self.__iter = iter(self.__map) return self def __next__(self): return next(self.__iter) def __len__(self): return len(self.__map) def __getitem__(self, context): return self._record_for(context) def keys(self): ''' Names of contexts/keys in this reference. ''' return self.__map.keys() def items(self): ''' Items iterator for this reference. ''' return self.__map.items() @property def _map(self): return self.__map @property def path(self): ''' Path of this reference file. ''' return self.__path @property def name(self): ''' Name of this reference. ''' return self.__name def _update(self, data_reference): for context, record in data_reference.map.items(): if context not in self.map: self.__map[context] = CIStringDict() self.__map[context].update(record.named_values) def _update_from_dict(self, context, map): if context not in self.map: self.__map[context] = dict() self.__map[context].update(map) def _add_record(self, context, record): self.__map[context] = DataRecord(context="Ref-{}[{}]".format( self.name, context), **record) def _record_for(self, context): try: return self.__map[context] except: raise Exception("{} at {} does not contain {} context key.".format( self.__class__.__name__, self.path, context)) def __str__(self): return str({k: str(v) for k, v in self.__map.items()}) def enumerate(self): ''' Print all items in this data reference. ''' for k, v in self.__map.items(): print(k, "::", type(v), str(v)) @abc.abstractmethod def _populate(self): pass def as_json(self): from arjuna import Json, Http return Json.from_str(Http.content.json(self.named_values).content)
class Yaml: def __init__(self, *, name, pydict, file_path=None): self.__name = name self.__ydict = pydict is not None and pydict or dict() self.__sections = tuple(self.__ydict.keys()) self.__ydict = CIStringDict(self.__ydict) self.__file_path = file_path @property def name(self): return self.__name @property def file_path(self): return self.__file_path def is_empty(self): return not self.__ydict def get_section(self, name, *, strict=True): val = self.get_value(name, strict=strict) if val is not None and type(val) is not dict: raise YamlError( f"Section content must be a dictionary. Found content >>{val}<< in {name} section." ) return Yaml(name=name, pydict=val, file_path=self.file_path) def get_value(self, name, *, strict=True, as_yaml_str=False): if self.has_section(name): if as_yaml_str: return yaml.dump(self.__ydict[name]) else: return self.__ydict[name] else: if strict: raise YamlUndefinedSectionError( f"Yaml object does not have a section with the name: {name}" ) else: return None def as_map(self): return self.__ydict def has_section(self, name): return name in self.__ydict @property def section_names(self): return self.__ydict.keys() def validate_sections_present(*section_names, atleast_one=False): absent_sections = [] present_section_names = self.section_names for section_name in section_names: if section_name not in present_section_names: absent_sections.append(section_name) if len(absent_sections) == section_names or ( len(absent_sections) < len(section_names) and not atleast_one): raise YamlUndefinedSectionError( f"Yaml object does not contains mandatory sections: {absent_sections}" ) @classmethod def from_file(cls, *, file_path): yaml_name = os.path.basename(file_path).split(".yaml")[0] f = open(file_path, "r") ydict = yaml.load(f, Loader=yaml.SafeLoader) f.close() return Yaml(name=yaml_name, pydict=ydict, file_path=file_path) @classmethod def from_str(cls, *, name, contents): return Yaml(name=name, pydict=yaml.safe_load(contents)) def as_str(self): return yaml.dump(self.__ydict)