diff --git a/.coveragerc b/.coveragerc index baa10de454893675aeedc6275e2c6725b0b84966..69c1dcd3f1ca5068733a54fdb231bab80170169d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -23,3 +23,6 @@ exclude_lines = # Don't complain about import statements import + + # Don't complain about abstract class declarations and placeholders + pass diff --git a/src/datastore.py b/src/datastore.py index 4c71e67facd48abe4f5e9097615a362944376498..b106c767dab5ee937c4df288995c60ae16d35b1d 100644 --- a/src/datastore.py +++ b/src/datastore.py @@ -2,17 +2,21 @@ __author__ = 'Lukas Leufen' __date__ = '2019-11-22' -from typing import Any +from typing import Any, List from abc import ABC class NameNotFoundInDataStore(Exception): - + """ + Exception that get raised if given name is not found in the entire data store. + """ pass class NameNotFoundInScope(Exception): - + """ + Exception that get raised if given name is not found in the provided scope, but can be found in other scopes. + """ pass @@ -28,21 +32,72 @@ class AbstractDataStore(ABC): self._store = {} def put(self, name: str, obj: Any, scope: str) -> None: + """ + Abstract method to add an object to the data store + :param name: Name of object to store + :param obj: The object itself to be stored + :param scope: the scope / context of the object, under that the object is valid + """ pass def get(self, name: str, scope: str) -> None: + """ + Abstract method to get an object from the data store + :param name: Name to look for + :param scope: scope to search the name for + :return: the stored object + """ + pass + + def search_name(self, name: str) -> None: + """ + Abstract method to search for all occurrences of given `name` in the entire data store. + :param name: Name to look for + :return: search result + """ pass class DataStoreByVariable(AbstractDataStore): - def put(self, name, obj, scope): + """ + Data store for all settings for the experiment workflow to save experiment parameters for the proceeding modules + and predefine parameters loaded during the experiment setup phase. The data store is hierarchically structured, so + that global settings can be overwritten by local adjustments. + + This implementation stores data as + <variable1> + <scope1>: value + <scope2>: value + <variable2> + <scope1>: value + <scope3>: value + """ + + def put(self, name: str, obj: Any, scope: str) -> None: + """ + Store an object `obj` with given `name` under `scope`. In the current implementation, existing entries are + overwritten. + :param name: Name of object to store + :param obj: The object itself to be stored + :param scope: the scope / context of the object, under that the object is valid + """ # open new variable related store with `name` as key if not existing if name not in self._store.keys(): self._store[name] = {} self._store[name][scope] = obj - def get(self, name, scope, depth=None): + def get(self, name: str, scope: str, depth: int = None) -> Any: + """ + Retrieve an object with `name` from `scope`. If no object can be found in the exact scope, take an iterative + look on the levels above. Raises a NameNotFoundInDataStore error, if no object with given name can be found in + the entire data store. Raises a NameNotFoundInScope error, if the object is in the data store but not in the + given scope and its levels above (could be either included in another scope or a more detailed sub-scope). + :param name: Name to look for + :param scope: scope to search the name for + :param depth: counter to check, if all roots of the scope have been visited to trigger an error. + :return: the stored object + """ if depth is None: depth = scope.count(".") if depth >= 0: @@ -57,18 +112,54 @@ class DataStoreByVariable(AbstractDataStore): else: raise NameNotFoundInDataStore(f"Couldn't find {name} in data store") - def search_name(self, name): + def search_name(self, name: str) -> List[str]: + """ + Search for all occurrences of given `name` in the entire data store. + :param name: Name to look for + :return: list with all scopes and sub-scopes containing an object stored as `name` + """ return sorted(self._store[name]) class DataStoreByScope(AbstractDataStore): - def put(self, name, obj, scope): + """ + Data store for all settings for the experiment workflow to save experiment parameters for the proceeding modules + and predefine parameters loaded during the experiment setup phase. The data store is hierarchically structured, so + that global settings can be overwritten by local adjustments. + + This implementation stores data as + <scope1> + <variable1>: value + <variable2>: value + <scope2> + <variable1>: value + <variable3>: value + """ + + def put(self, name: str, obj: Any, scope: str) -> None: + """ + Store an object `obj` with given `name` under `scope`. In the current implementation, existing entries are + overwritten. + :param name: Name of object to store + :param obj: The object itself to be stored + :param scope: the scope / context of the object, under that the object is valid + """ if scope not in self._store.keys(): self._store[scope] = {} self._store[scope][name] = obj - def get(self, name, scope, depth=None): + def get(self, name: str, scope: str, depth: int = None) -> Any: + """ + Retrieve an object with `name` from `scope`. If no object can be found in the exact scope, take an iterative + look on the levels above. Raises a NameNotFoundInDataStore error, if no object with given name can be found in + the entire data store. Raises a NameNotFoundInScope error, if the object is in the data store but not in the + given scope and its levels above (could be either included in another scope or a more detailed sub-scope). + :param name: Name to look for + :param scope: scope to search the name for + :param depth: counter to check, if all roots of the scope have been visited to trigger an error. + :return: the stored object + """ if depth is None: depth = scope.count(".") if depth >= 0: @@ -84,9 +175,16 @@ class DataStoreByScope(AbstractDataStore): raise NameNotFoundInScope(f"Couldn't find {name} in scope {scope}. {name} is only defined in " f"{occurrences}") - def search_name(self, name): + def search_name(self, name: str) -> List[str]: + """ + Search for all occurrences of given `name` in the entire data store. + :param name: Name to look for + :return: list with all scopes and sub-scopes containing an object stored as `name` + """ keys = [] for (key, val) in self._store.items(): if name in val.keys(): keys.append(key) return sorted(keys) + +