diff --git a/src/datastore.py b/src/datastore.py index b106c767dab5ee937c4df288995c60ae16d35b1d..c6f200bb081ac6ad5ab7661d5cc4419098ca0c1d 100644 --- a/src/datastore.py +++ b/src/datastore.py @@ -20,6 +20,13 @@ class NameNotFoundInScope(Exception): pass +class EmptyScope(Exception): + """ + Exception that get raised if given scope is not part of the data store. + """ + pass + + class AbstractDataStore(ABC): """ @@ -57,6 +64,21 @@ class AbstractDataStore(ABC): """ pass + def search_scope(self, scope: str) -> None: + """ + Abstract method to search for all object names that are stored for given scope + :param scope: scope to look for + :return: search result + """ + pass + + def list_all_scopes(self) -> None: + """ + Abstract method to list all scopes in data store + :return: all found scopes + """ + pass + class DataStoreByVariable(AbstractDataStore): @@ -106,10 +128,11 @@ class DataStoreByVariable(AbstractDataStore): except KeyError: return self.get(name, scope.rsplit(".", maxsplit=depth)[0], depth-1) else: - if name in self._store.keys(): + try: + occurrences = self.search_name(name) raise NameNotFoundInScope(f"Couldn't find {name} in scope {scope}. {name} is only defined in " - f"{self.search_name(name)}") - else: + f"{occurrences}") + except KeyError: raise NameNotFoundInDataStore(f"Couldn't find {name} in data store") def search_name(self, name: str) -> List[str]: @@ -120,6 +143,34 @@ class DataStoreByVariable(AbstractDataStore): """ return sorted(self._store[name]) + def search_scope(self, scope: str) -> List[str]: + """ + Search for given `scope` and list all object names stored under this scope. + :param scope: scope to look for + :return: list with all object names + """ + names = [] + for (k, v) in self._store.items(): + if scope in v.keys(): + names.append(k) + if len(names) > 0: + return sorted(names) + else: + raise EmptyScope(f"Given scope {scope} is not part of the data store. Available scopes are: " + f"{self.list_all_scopes()}") + + def list_all_scopes(self) -> List[str]: + """ + List all available scopes in data store + :return: names of all stored objects + """ + scopes = [] + for v in self._store.values(): + for scope in v.keys(): + if scope not in scopes: + scopes.append(scope) + return sorted(scopes) + class DataStoreByScope(AbstractDataStore): @@ -175,6 +226,8 @@ 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: str) -> List[str]: """ Search for all occurrences of given `name` in the entire data store. @@ -187,4 +240,22 @@ class DataStoreByScope(AbstractDataStore): keys.append(key) return sorted(keys) + def search_scope(self, scope: str, current_scope_only=True) -> List[str]: + """ + Search for given `scope` and list all object names stored under this scope. + :param scope: scope to look for + :return: list with all object names + """ + try: + return sorted(self._store[scope].keys()) + except KeyError: + raise EmptyScope(f"Given scope {scope} is not part of the data store. Available scopes are: " + f"{self.list_all_scopes()}") + + def list_all_scopes(self) -> List[str]: + """ + List all available scopes in data store + :return: names of all stored objects + """ + return sorted(self._store.keys()) diff --git a/test/test_datastore.py b/test/test_datastore.py index 1c41b18c1454fc5c5db7d5e9fcac38d15f260b85..1434508b6d033aa0e915ace6bcc1eafb0febe638 100644 --- a/test/test_datastore.py +++ b/test/test_datastore.py @@ -3,7 +3,7 @@ __date__ = '2019-11-22' from src.datastore import AbstractDataStore, DataStoreByVariable, DataStoreByScope -from src.datastore import NameNotFoundInDataStore, NameNotFoundInScope +from src.datastore import NameNotFoundInDataStore, NameNotFoundInScope, EmptyScope import pytest @@ -61,6 +61,28 @@ class TestDataStoreByVariable: ds.get("number", "general") assert "Couldn't find number in scope general. number is only defined in ['general.sub']" in e.value.args[0] + def test_list_all_scopes(self, ds): + ds.put("number", 22, "general2") + ds.put("number", 11, "general.sub") + ds.put("number", 3, "general.sub3") + ds.put("number", 1, "general") + assert ds.list_all_scopes() == ['general', 'general.sub', 'general.sub3', 'general2'] + + def test_search_scope(self, ds): + ds.put("number", 22, "general") + ds.put("number", 11, "general.sub") + ds.put("number1", 22, "general.sub") + ds.put("number2", 3, "general.sub.sub") + assert ds.search_scope("general.sub") == ["number", "number1"] + + def test_search_empty_scope(self, ds): + ds.put("number", 22, "general2") + ds.put("number", 11, "general.sub") + with pytest.raises(EmptyScope) as e: + ds.search_scope("general.sub2") + assert "Given scope general.sub2 is not part of the data store." in e.value.args[0] + assert "Available scopes are: ['general.sub', 'general2']" in e.value.args[0] + class TestDataStoreByScope: @@ -105,3 +127,34 @@ class TestDataStoreByScope: with pytest.raises(NameNotFoundInScope) as e: ds.get("number", "general") assert "Couldn't find number in scope general. number is only defined in ['general.sub']" in e.value.args[0] + + def test_list_all_scopes(self, ds): + ds.put("number", 22, "general2") + ds.put("number", 11, "general.sub") + ds.put("number", 3, "general.sub3") + ds.put("number", 1, "general") + assert ds.list_all_scopes() == ['general', 'general.sub', 'general.sub3', 'general2'] + + def test_search_scope(self, ds): + ds.put("number", 22, "general") + ds.put("number", 11, "general.sub") + ds.put("number1", 22, "general.sub") + ds.put("number2", 3, "general.sub.sub") + assert ds.search_scope("general.sub") == ["number", "number1"] + + def test_search_empty_scope(self, ds): + ds.put("number", 22, "general2") + ds.put("number", 11, "general.sub") + with pytest.raises(EmptyScope) as e: + ds.search_scope("general.sub2") + assert "Given scope general.sub2 is not part of the data store." in e.value.args[0] + assert "Available scopes are: ['general.sub', 'general2']" in e.value.args[0] + + def test_search_scope_and_all_superiors(self, ds): + ds.put("number", 22, "general") + ds.put("number", 11, "general.sub") + ds.put("number1", 22, "general.sub") + ds.put("number2", 3, "general.sub.sub") + assert ds.search_scope("general.sub", current_scope_only=False) == ["number", "number1"] + assert ds.search_scope("general.sub.sub", current_scope_only=False) == ["number", "number1", "number2"] +