From 22498ceaaf49024b78df05087072f9cd336ca32f Mon Sep 17 00:00:00 2001 From: lukas leufen <l.leufen@fz-juelich.de> Date: Fri, 22 Nov 2019 16:42:51 +0100 Subject: [PATCH] first implementation of data store with 2 different designs but same functionality, not decided yet which representation is more suitable --- src/datastore.py | 92 +++++++++++++++++++++++++++++++++++ test/test_datastore.py | 107 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 src/datastore.py create mode 100644 test/test_datastore.py diff --git a/src/datastore.py b/src/datastore.py new file mode 100644 index 00000000..4c71e67f --- /dev/null +++ b/src/datastore.py @@ -0,0 +1,92 @@ +__author__ = 'Lukas Leufen' +__date__ = '2019-11-22' + + +from typing import Any +from abc import ABC + + +class NameNotFoundInDataStore(Exception): + + pass + + +class NameNotFoundInScope(Exception): + + pass + + +class AbstractDataStore(ABC): + + """ + 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. + """ + def __init__(self): + # empty initialise the data-store variables + self._store = {} + + def put(self, name: str, obj: Any, scope: str) -> None: + pass + + def get(self, name: str, scope: str) -> None: + pass + + +class DataStoreByVariable(AbstractDataStore): + + def put(self, name, obj, scope): + # 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): + if depth is None: + depth = scope.count(".") + if depth >= 0: + try: + return self._store[name][scope] + except KeyError: + return self.get(name, scope.rsplit(".", maxsplit=depth)[0], depth-1) + else: + if name in self._store.keys(): + raise NameNotFoundInScope(f"Couldn't find {name} in scope {scope}. {name} is only defined in " + f"{self.search_name(name)}") + else: + raise NameNotFoundInDataStore(f"Couldn't find {name} in data store") + + def search_name(self, name): + return sorted(self._store[name]) + + +class DataStoreByScope(AbstractDataStore): + + def put(self, name, obj, scope): + if scope not in self._store.keys(): + self._store[scope] = {} + self._store[scope][name] = obj + + def get(self, name, scope, depth=None): + if depth is None: + depth = scope.count(".") + if depth >= 0: + try: + return self._store[scope][name] + except KeyError: + return self.get(name, scope.rsplit(".", maxsplit=1)[0], depth-1) + else: + occurrences = self.search_name(name) + if len(occurrences) == 0: + raise NameNotFoundInDataStore(f"Couldn't find {name} in data store") + else: + raise NameNotFoundInScope(f"Couldn't find {name} in scope {scope}. {name} is only defined in " + f"{occurrences}") + + def search_name(self, name): + keys = [] + for (key, val) in self._store.items(): + if name in val.keys(): + keys.append(key) + return sorted(keys) diff --git a/test/test_datastore.py b/test/test_datastore.py new file mode 100644 index 00000000..1c41b18c --- /dev/null +++ b/test/test_datastore.py @@ -0,0 +1,107 @@ +__author__ = 'Lukas Leufen' +__date__ = '2019-11-22' + + +from src.datastore import AbstractDataStore, DataStoreByVariable, DataStoreByScope +from src.datastore import NameNotFoundInDataStore, NameNotFoundInScope +import pytest + + +class TestAbstractDataStore: + + @pytest.fixture + def ds(self): + return AbstractDataStore() + + def test_init(self, ds): + assert ds._store == {} + + +class TestDataStoreByVariable: + + @pytest.fixture + def ds(self): + return DataStoreByVariable() + + def test_put(self, ds): + ds.put("number", 3, "general.subscope") + assert ds._store["number"]["general.subscope"] == 3 + + def test_get(self, ds): + ds.put("number", 3, "general.subscope") + assert ds.get("number", "general.subscope") == 3 + + def test_get_with_sub_scope(self, ds): + ds.put("number", 3, "general") + ds.put("number", 10, "general.subscope") + assert ds.get("number", "general.subscope") == 10 + assert ds.get("number", "general") == 3 + + def test_get_with_not_existing_sub_scope(self, ds): + ds.put("number", 3, "general") + ds.put("number2", 10, "general.subscope") + ds.put("number2", 1, "general") + assert ds.get("number", "general.subscope") == 3 + + def test_raise_not_in_data_store(self, ds): + ds.put("number", 22, "general") + with pytest.raises(NameNotFoundInDataStore) as e: + ds.get("number3", "general") + assert "Couldn't find number3 in data store" in e.value.args[0] + + def test_search(self, ds): + ds.put("number", 22, "general") + ds.put("number", 22, "general2") + ds.put("number", 22, "general.sub") + assert ds.search_name("number") == ["general", "general.sub", "general2"] + + def test_raise_not_in_scope(self, ds): + ds.put("number", 11, "general.sub") + 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] + + +class TestDataStoreByScope: + + @pytest.fixture + def ds(self): + return DataStoreByScope() + + def test_put_with_scope(self, ds): + ds.put("number", 3, "general.subscope") + assert ds._store["general.subscope"]["number"] == 3 + + def test_get(self, ds): + ds.put("number", 3, "general.subscope") + assert ds.get("number", "general.subscope") == 3 + + def test_get_with_sub_scope(self, ds): + ds.put("number", 3, "general") + ds.put("number", 10, "general.subscope") + assert ds.get("number", "general.subscope") == 10 + assert ds.get("number", "general") == 3 + + def test_get_with_not_existing_sub_scope(self, ds): + ds.put("number", 3, "general") + ds.put("number2", 10, "general.subscope") + ds.put("number2", 1, "general") + assert ds.get("number", "general.subscope") == 3 + + def test_raise_not_in_data_store(self, ds): + ds.put("number", 22, "general") + with pytest.raises(NameNotFoundInDataStore) as e: + ds.get("number3", "general") + assert "Couldn't find number3 in data store" in e.value.args[0] + + def test_search(self, ds): + ds.put("number", 22, "general") + ds.put("number", 22, "general2") + ds.put("number", 22, "general.sub") + assert ds.search_name("number") == ["general", "general.sub", "general2"] + + def test_raise_not_in_scope(self, ds): + ds.put("number", 11, "general.sub") + 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] -- GitLab