aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Baumgartner <dev@igsor.net>2022-12-18 14:17:44 +0100
committerMatthias Baumgartner <dev@igsor.net>2022-12-18 14:17:44 +0100
commit12d95ed8bda18f2ef9d36190919cb838bfb5efcf (patch)
tree84202e614a9f477e9bff3a4f20456ebc8c000321
parent8ed8dbb4010a9a75cf6e61d185327825fe783776 (diff)
downloadbsfs-12d95ed8bda18f2ef9d36190919cb838bfb5efcf.tar.gz
bsfs-12d95ed8bda18f2ef9d36190919cb838bfb5efcf.tar.bz2
bsfs-12d95ed8bda18f2ef9d36190919cb838bfb5efcf.zip
bsfs lib and builders
-rw-r--r--bsfs/__init__.py15
-rw-r--r--bsfs/front/__init__.py20
-rw-r--r--bsfs/front/bsfs.py29
-rw-r--r--bsfs/front/builder.py75
-rw-r--r--test/front/__init__.py0
-rw-r--r--test/front/test_bsfs.py38
-rw-r--r--test/front/test_builder.py64
7 files changed, 241 insertions, 0 deletions
diff --git a/bsfs/__init__.py b/bsfs/__init__.py
index f5f5cbc..079ffaf 100644
--- a/bsfs/__init__.py
+++ b/bsfs/__init__.py
@@ -4,5 +4,20 @@ Part of the BlackStar filesystem (bsfs) module.
A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2022
"""
+# imports
+import collections
+import typing
+
+# bsfs imports
+from .front import Open
+
+# constants
+T_VERSION_INFO = collections.namedtuple('T_VERSION_INFO', ('major', 'minor', 'micro')) # pylint: disable=invalid-name
+version_info = T_VERSION_INFO(0, 0, 1)
+
+# exports
+__all__: typing.Sequence[str] = (
+ 'Open',
+ )
## EOF ##
diff --git a/bsfs/front/__init__.py b/bsfs/front/__init__.py
new file mode 100644
index 0000000..92886ab
--- /dev/null
+++ b/bsfs/front/__init__.py
@@ -0,0 +1,20 @@
+"""
+
+Part of the BlackStar filesystem (bsfs) module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import typing
+
+# inner-module imports
+from .bsfs import Open
+from .builder import build_graph
+
+# exports
+__all__: typing.Sequence[str] = (
+ 'Open',
+ 'build_graph',
+ )
+
+## EOF ##
diff --git a/bsfs/front/bsfs.py b/bsfs/front/bsfs.py
new file mode 100644
index 0000000..968b3f5
--- /dev/null
+++ b/bsfs/front/bsfs.py
@@ -0,0 +1,29 @@
+"""
+
+Part of the BlackStar filesystem (bsfs) module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import typing
+
+# bsfs imports
+from bsfs.graph import Graph
+
+# inner-module imports
+from . import builder
+
+# exports
+__all__: typing.Sequence[str] = (
+ 'Open',
+ )
+
+
+## code ##
+
+# NOTE: Capitalized to mark entry point and to separate from builtin open.
+def Open(cfg: typing.Any) -> Graph: # pylint: disable=invalid-name
+ """Open a BSFS storage and return a `bsfs.graph.Graph` instance."""
+ return builder.build_graph(cfg)
+
+## EOF ##
diff --git a/bsfs/front/builder.py b/bsfs/front/builder.py
new file mode 100644
index 0000000..73f1703
--- /dev/null
+++ b/bsfs/front/builder.py
@@ -0,0 +1,75 @@
+"""
+
+Part of the BlackStar filesystem (bsfs) module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import typing
+
+# bsfs imports
+from bsfs.graph import Graph
+from bsfs.triple_store import TripleStoreBase, SparqlStore
+from bsfs.utils import URI, errors
+
+# exports
+__all__: typing.Sequence[str] = (
+ 'build_graph',
+ )
+
+# constants
+_graph_classes = {
+ 'Graph': Graph,
+ }
+
+_backend_classes = {
+ 'SparqlStore': SparqlStore,
+ }
+
+
+## code ##
+
+def build_backend(cfg: typing.Any) -> TripleStoreBase:
+ """Build and return a backend from user-provided config."""
+ # essential checks
+ if not isinstance(cfg, dict):
+ raise TypeError(cfg)
+ if len(cfg) != 1:
+ raise errors.ConfigError(f'expected a single key that identifies the backend class, found {list(cfg)}')
+ # unpack from config
+ name = next(iter(cfg))
+ args = cfg[name]
+ # check name
+ if name not in _backend_classes:
+ raise errors.ConfigError(f'{name} is not a valid triple store class name')
+ # build and return backend
+ cls = _backend_classes[name]
+ return cls.Open(**args)
+
+
+def build_graph(cfg: typing.Any) -> Graph:
+ """Build and return a Graph from user-provided config."""
+ # essential checks
+ if not isinstance(cfg, dict):
+ raise TypeError(cfg)
+ if len(cfg) != 1:
+ raise errors.ConfigError(f'expected a single key that identifies the graph class, found {list(cfg)}')
+ # unpack from config
+ name = next(iter(cfg))
+ args = cfg[name]
+ # check name
+ if name not in _graph_classes:
+ raise errors.ConfigError(f'{name} is not a valid graph class name')
+ # check user argument
+ if 'user' not in args:
+ raise errors.ConfigError('required argument "user" is not provided')
+ user = URI(args['user'])
+ # check backend argument
+ if 'backend' not in args:
+ raise errors.ConfigError('required argument "backend" is not provided')
+ backend = build_backend(args['backend'])
+ # build and return graph
+ cls = _graph_classes[name]
+ return cls(backend, user)
+
+## EOF ##
diff --git a/test/front/__init__.py b/test/front/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/front/__init__.py
diff --git a/test/front/test_bsfs.py b/test/front/test_bsfs.py
new file mode 100644
index 0000000..0d7f383
--- /dev/null
+++ b/test/front/test_bsfs.py
@@ -0,0 +1,38 @@
+"""
+
+Part of the bsfs test suite.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import unittest
+
+# bsie imports
+from bsfs.graph import Graph
+from bsfs.triple_store import SparqlStore
+from bsfs.utils import errors, URI
+
+# objects to test
+from bsfs.front.bsfs import Open
+
+
+## code ##
+
+class TestBSFS(unittest.TestCase):
+ def test_open(self):
+ # valid config produces a valid graph
+ config = {'Graph': {'backend': {'SparqlStore': {}}, 'user': 'http://example.com/me'}}
+ graph = Open(config)
+ self.assertIsInstance(graph, Graph)
+ self.assertIsInstance(graph._backend, SparqlStore)
+ self.assertEqual(graph._user, URI('http://example.com/me'))
+ # invalid config raises an error
+ self.assertRaises(errors.ConfigError, Open, {})
+
+
+## main ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+## EOF ##
diff --git a/test/front/test_builder.py b/test/front/test_builder.py
new file mode 100644
index 0000000..08f2027
--- /dev/null
+++ b/test/front/test_builder.py
@@ -0,0 +1,64 @@
+"""
+
+Part of the bsfs test suite.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import unittest
+
+# bsie imports
+from bsfs.graph import Graph
+from bsfs.triple_store import SparqlStore
+from bsfs.utils import errors, URI
+
+# objects to test
+from bsfs.front.builder import build_backend, build_graph
+
+
+## code ##
+
+class TestBuilder(unittest.TestCase):
+ def test_build_backend(self):
+ # valid config produces a valid store
+ store = build_backend({'SparqlStore': {}})
+ self.assertIsInstance(store, SparqlStore)
+ self.assertIsNone(store.uri)
+ # cannot create an invalid store
+ self.assertRaises(errors.ConfigError, build_backend, {'MyStore': {}})
+ # must pass a dict
+ self.assertRaises(TypeError, build_backend, 1234)
+ self.assertRaises(TypeError, build_backend, 'hello world')
+ self.assertRaises(TypeError, build_backend, [1,2,3])
+ # cannot create a store from an invalid config
+ self.assertRaises(errors.ConfigError, build_backend, {})
+ self.assertRaises(errors.ConfigError, build_backend, {'SparqlStore': {}, 'OtherStore': {}})
+ self.assertRaises(TypeError, build_backend, {'SparqlStore': {'hello': 'world'}})
+
+ def test_build_graph(self):
+ # valid config produces a valid graph
+ graph = build_graph({'Graph': {'backend': {'SparqlStore': {}}, 'user': 'http://example.com/me'}})
+ self.assertIsInstance(graph, Graph)
+ self.assertIsInstance(graph._backend, SparqlStore)
+ self.assertEqual(graph._user, URI('http://example.com/me'))
+ # cannot create an invalid graph
+ self.assertRaises(errors.ConfigError, build_graph, {'MyGraph': {}})
+ # must pass a dict
+ self.assertRaises(TypeError, build_graph, 1234)
+ self.assertRaises(TypeError, build_graph, 'hello world')
+ self.assertRaises(TypeError, build_graph, [1,2,3])
+ # cannot create a graph from an invalid config
+ self.assertRaises(errors.ConfigError, build_graph, {})
+ self.assertRaises(errors.ConfigError, build_graph, {'Graph': {}, 'Graph2': {}})
+ self.assertRaises(errors.ConfigError, build_graph, {'Graph': {}})
+ self.assertRaises(errors.ConfigError, build_graph, {'Graph': {'user': 'http://example.com/me'}})
+ self.assertRaises(errors.ConfigError, build_graph, {'Graph': {'backend': 'Hello world'}})
+ self.assertRaises(TypeError, build_graph, {'Graph': {'user': 'http://example.com/me', 'backend': 'Hello world'}})
+
+
+## main ##
+
+if __name__ == '__main__':
+ unittest.main()
+
+## EOF ##