aboutsummaryrefslogtreecommitdiffstats
path: root/bsfs
diff options
context:
space:
mode:
Diffstat (limited to 'bsfs')
-rw-r--r--bsfs/__init__.py5
-rw-r--r--bsfs/apps/__init__.py43
-rw-r--r--bsfs/apps/init.py11
-rw-r--r--bsfs/apps/migrate.py6
-rw-r--r--bsfs/front/__init__.py5
-rw-r--r--bsfs/front/bsfs.py5
-rw-r--r--bsfs/front/builder.py11
-rw-r--r--bsfs/graph/__init__.py5
-rw-r--r--bsfs/graph/ac/__init__.py5
-rw-r--r--bsfs/graph/ac/base.py27
-rw-r--r--bsfs/graph/ac/null.py13
-rw-r--r--bsfs/graph/graph.py100
-rw-r--r--bsfs/graph/nodes.py113
-rw-r--r--bsfs/graph/resolve.py27
-rw-r--r--bsfs/graph/result.py5
-rw-r--r--bsfs/graph/schema.nt13
-rw-r--r--bsfs/graph/walk.py5
-rw-r--r--bsfs/namespace/__init__.py8
-rw-r--r--bsfs/namespace/namespace.py102
-rw-r--r--bsfs/namespace/predefined.py32
-rw-r--r--bsfs/query/__init__.py5
-rw-r--r--bsfs/query/ast/__init__.py3
-rw-r--r--bsfs/query/ast/fetch.py5
-rw-r--r--bsfs/query/ast/filter_.py6
-rw-r--r--bsfs/query/matcher.py9
-rw-r--r--bsfs/query/validator.py21
-rw-r--r--bsfs/schema/__init__.py5
-rw-r--r--bsfs/schema/schema.py5
-rw-r--r--bsfs/schema/serialize.py20
-rw-r--r--bsfs/schema/types.py21
-rw-r--r--bsfs/triple_store/__init__.py5
-rw-r--r--bsfs/triple_store/base.py5
-rw-r--r--bsfs/triple_store/sparql/__init__.py5
-rw-r--r--bsfs/triple_store/sparql/distance.py11
-rw-r--r--bsfs/triple_store/sparql/parse_fetch.py5
-rw-r--r--bsfs/triple_store/sparql/parse_filter.py13
-rw-r--r--bsfs/triple_store/sparql/sparql.py15
-rw-r--r--bsfs/triple_store/sparql/utils.py8
-rw-r--r--bsfs/utils/__init__.py5
-rw-r--r--bsfs/utils/commons.py5
-rw-r--r--bsfs/utils/errors.py5
-rw-r--r--bsfs/utils/uri.py13
-rw-r--r--bsfs/utils/uuid.py5
43 files changed, 306 insertions, 435 deletions
diff --git a/bsfs/__init__.py b/bsfs/__init__.py
index 079ffaf..cf08d64 100644
--- a/bsfs/__init__.py
+++ b/bsfs/__init__.py
@@ -1,9 +1,4 @@
-"""
-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
diff --git a/bsfs/apps/__init__.py b/bsfs/apps/__init__.py
index 7efaa87..62dc5b5 100644
--- a/bsfs/apps/__init__.py
+++ b/bsfs/apps/__init__.py
@@ -1,20 +1,53 @@
-"""
+#!/usr/bin/env python3
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
+import argparse
import typing
+# bsfs imports
+import bsfs
+
# inner-module imports
from .init import main as init
from .migrate import main as migrate
# exports
__all__: typing.Sequence[str] = (
+ 'main',
'init',
'migrate',
)
+# config
+apps = {
+ 'init' : init,
+ 'migrate' : migrate,
+ }
+
+
+## code ##
+
+def main(argv=None):
+ """Black Star File System maintenance tools."""
+ parser = argparse.ArgumentParser(description=main.__doc__, prog='bsfs')
+ # version
+ parser.add_argument('--version', action='version',
+ version='%(prog)s version {}.{}.{}'.format(*bsfs.version_info)) # pylint: disable=C0209
+ # application selection
+ parser.add_argument('app', choices=apps.keys(),
+ help='Select the application to run.')
+ # dangling args
+ parser.add_argument('rest', nargs=argparse.REMAINDER)
+ # parse
+ args = parser.parse_args(argv)
+ # run application
+ apps[args.app](args.rest)
+
+
+## main ##
+
+if __name__ == '__main__':
+ import sys
+ main(sys.argv[1:])
+
## EOF ##
diff --git a/bsfs/apps/init.py b/bsfs/apps/init.py
index 3e2ef37..9afbdd5 100644
--- a/bsfs/apps/init.py
+++ b/bsfs/apps/init.py
@@ -1,9 +1,5 @@
-"""
+#!/usr/bin/env python3
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import argparse
import json
@@ -60,9 +56,10 @@ def main(argv):
# print config
if args.output is not None:
with open(args.output, mode='wt', encoding='UTF-8') as ofile:
- json.dump(config, ofile)
+ json.dump(config, ofile, indent=4)
else:
- json.dump(config, sys.stdout)
+ json.dump(config, sys.stdout, indent=4)
+ print('')
## main ##
diff --git a/bsfs/apps/migrate.py b/bsfs/apps/migrate.py
index b9d019f..34ea2e7 100644
--- a/bsfs/apps/migrate.py
+++ b/bsfs/apps/migrate.py
@@ -1,9 +1,5 @@
-"""
+#!/usr/bin/env python3
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import argparse
import json
diff --git a/bsfs/front/__init__.py b/bsfs/front/__init__.py
index 92886ab..cedcd7f 100644
--- a/bsfs/front/__init__.py
+++ b/bsfs/front/__init__.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/front/bsfs.py b/bsfs/front/bsfs.py
index 968b3f5..f437212 100644
--- a/bsfs/front/bsfs.py
+++ b/bsfs/front/bsfs.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/front/builder.py b/bsfs/front/builder.py
index 73f1703..b1d488b 100644
--- a/bsfs/front/builder.py
+++ b/bsfs/front/builder.py
@@ -1,14 +1,9 @@
-"""
-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.graph import Graph, ac
from bsfs.triple_store import TripleStoreBase, SparqlStore
from bsfs.utils import URI, errors
@@ -68,8 +63,10 @@ def build_graph(cfg: typing.Any) -> Graph:
if 'backend' not in args:
raise errors.ConfigError('required argument "backend" is not provided')
backend = build_backend(args['backend'])
+ # build access controls
+ access_controls = ac.NullAC(backend, user)
# build and return graph
cls = _graph_classes[name]
- return cls(backend, user)
+ return cls(backend, access_controls)
## EOF ##
diff --git a/bsfs/graph/__init__.py b/bsfs/graph/__init__.py
index 82d2235..8d38d23 100644
--- a/bsfs/graph/__init__.py
+++ b/bsfs/graph/__init__.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/graph/ac/__init__.py b/bsfs/graph/ac/__init__.py
index 420de01..11b45df 100644
--- a/bsfs/graph/ac/__init__.py
+++ b/bsfs/graph/ac/__init__.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/graph/ac/base.py b/bsfs/graph/ac/base.py
index 79b09e5..e85c1dd 100644
--- a/bsfs/graph/ac/base.py
+++ b/bsfs/graph/ac/base.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import abc
import typing
@@ -12,7 +7,7 @@ import typing
from bsfs import schema
from bsfs.query import ast
from bsfs.triple_store import TripleStoreBase
-from bsfs.utils import URI
+from bsfs.utils import URI, typename
# exports
__all__: typing.Sequence[str] = (
@@ -44,6 +39,20 @@ class AccessControlBase(abc.ABC):
self._backend = backend
self._user = URI(user)
+ def __str__(self) -> str:
+ return f'{typename(self)}({self._user})'
+
+ def __repr__(self) -> str:
+ return f'{typename(self)}({self._user})'
+
+ def __eq__(self, other: typing.Any) -> bool:
+ return isinstance(other, type(self)) \
+ and self._backend == other._backend \
+ and self._user == other._user
+
+ def __hash__(self) -> int:
+ return hash((type(self), self._backend, self._user))
+
@abc.abstractmethod
def is_protected_predicate(self, pred: schema.Predicate) -> bool:
"""Return True if a predicate cannot be modified manually."""
@@ -69,7 +78,11 @@ class AccessControlBase(abc.ABC):
"""Return nodes that are allowed to be created."""
@abc.abstractmethod
- def filter_read(self, node_type: schema.Node, query: ast.filter.FilterExpression) -> ast.filter.FilterExpression:
+ def filter_read(
+ self,
+ node_type: schema.Node,
+ query: typing.Optional[ast.filter.FilterExpression],
+ ) -> typing.Optional[ast.filter.FilterExpression]:
"""Re-write a filter *query* to get (i.e., read) *node_type* nodes."""
@abc.abstractmethod
diff --git a/bsfs/graph/ac/null.py b/bsfs/graph/ac/null.py
index 6a923a5..c9ec7d0 100644
--- a/bsfs/graph/ac/null.py
+++ b/bsfs/graph/ac/null.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
@@ -29,7 +24,7 @@ class NullAC(base.AccessControlBase):
def is_protected_predicate(self, pred: schema.Predicate) -> bool:
"""Return True if a predicate cannot be modified manually."""
- return pred.uri == ns.bsm.t_created
+ return pred.uri == ns.bsn.t_created
def create(self, node_type: schema.Node, guids: typing.Iterable[URI]):
"""Perform post-creation operations on nodes, e.g. ownership information."""
@@ -50,7 +45,11 @@ class NullAC(base.AccessControlBase):
"""Return nodes that are allowed to be created."""
return guids
- def filter_read(self, node_type: schema.Node, query: ast.filter.FilterExpression) -> ast.filter.FilterExpression:
+ def filter_read(
+ self,
+ node_type: schema.Node,
+ query: typing.Optional[ast.filter.FilterExpression]
+ ) -> typing.Optional[ast.filter.FilterExpression]:
"""Re-write a filter *query* to get (i.e., read) *node_type* nodes."""
return query
diff --git a/bsfs/graph/graph.py b/bsfs/graph/graph.py
index df2e3a5..1b4c212 100644
--- a/bsfs/graph/graph.py
+++ b/bsfs/graph/graph.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import os
import typing
@@ -28,9 +23,7 @@ __all__: typing.Sequence[str] = (
## code ##
class Graph():
- """The Graph class is
-
- The Graph class provides a convenient interface to query and access a graph.
+ """The Graph class provides a convenient interface to query and access a graph.
Since it logically builds on the concept of graphs it is easier to
navigate than raw triple stores. Naturally, it uses a triple store
as *backend*. It also controls actions via access permissions to a *user*.
@@ -40,31 +33,33 @@ class Graph():
# link to the triple storage backend.
_backend: TripleStoreBase
- # user uri.
- _user: URI
+ # access controls.
+ _ac: ac.AccessControlBase
- def __init__(self, backend: TripleStoreBase, user: URI):
+ def __init__(
+ self,
+ backend: TripleStoreBase,
+ access_control: ac.AccessControlBase,
+ ):
+ # store members
self._backend = backend
- self._user = user
- self._resolver = resolve.Filter(self._backend.schema)
- self._validate = validate.Filter(self._backend.schema)
- self._ac = ac.NullAC(self._backend, self._user)
+ self._ac = access_control
# ensure Graph schema requirements
self.migrate(self._backend.schema)
def __hash__(self) -> int:
- return hash((type(self), self._backend, self._user))
+ return hash((type(self), self._backend, self._ac))
def __eq__(self, other) -> bool:
return isinstance(other, type(self)) \
and self._backend == other._backend \
- and self._user == other._user
+ and self._ac == other._ac
def __repr__(self) -> str:
- return f'{typename(self)}(backend={repr(self._backend)}, user={self._user})'
+ return f'{typename(self)}({repr(self._backend)}, {self._ac})'
def __str__(self) -> str:
- return f'{typename(self)}({str(self._backend)}, {self._user})'
+ return f'{typename(self)}({str(self._backend)})'
@property
def schema(self) -> bsc.Schema:
@@ -90,9 +85,6 @@ class Graph():
# migrate schema in backend
# FIXME: consult access controls!
self._backend.schema = schema
- # re-initialize members
- self._resolver.schema = self.schema
- self._validate.schema = self.schema
# return self
return self
@@ -104,41 +96,69 @@ class Graph():
*node_type*) once some data is assigned to them.
"""
+ # get node type
type_ = self.schema.node(node_type)
# NOTE: Nodes constructor materializes guids.
- return _nodes.Nodes(self._backend, self._user, type_, guids)
+ return _nodes.Nodes(self._backend, self._ac, type_, guids)
def node(self, node_type: URI, guid: URI) -> _nodes.Nodes:
"""Return node *guid* of type *node_type* as a `bsfs.graph.Nodes` instance.
- Note that the *guids* need not to exist (however, the *node_type* has
+ Note that the *guid* need not to exist (however, the *node_type* has
to be part of the schema). An inexistent guid will be created (using
*node_type*) once some data is assigned to them.
"""
return self.nodes(node_type, {guid})
- def get(self, node_type: URI, query: ast.filter.FilterExpression) -> _nodes.Nodes: # FIXME: How about empty query?
- """Return a `Nodes` instance over all nodes of type *node_type* that match the *subject* query."""
- # get node type
- type_ = self.schema.node(node_type)
- # resolve Nodes instances
- query = self._resolver(type_, query)
- # add access controls to query
- query = self._ac.filter_read(type_, query)
- # validate query
- self._validate(type_, query)
- # query the backend
- guids = self._backend.get(type_, query) # no need to materialize
+ def empty(self, node_type: URI) -> _nodes.Nodes:
+ """Return a `Nodes` instance with type *node_type* but no nodes."""
+ return self.nodes(node_type, set())
+
+ def get(
+ self,
+ node_type: URI,
+ query: typing.Optional[ast.filter.FilterExpression],
+ ) -> _nodes.Nodes:
+ """Return a `Nodes` instance over all nodes of type *node_type* that match the *query*."""
# return Nodes instance
- return _nodes.Nodes(self._backend, self._user, type_, guids)
+ type_ = self.schema.node(node_type)
+ return _nodes.Nodes(self._backend, self._ac, type_, self.__get(node_type, query))
+
+ def sorted(
+ self,
+ node_type: URI,
+ query: typing.Optional[ast.filter.FilterExpression],
+ # FIXME: sort ast
+ ) -> typing.Iterator[_nodes.Nodes]:
+ """Return a iterator over `Nodes` instances over all nodes of type *node_type* that match the *query*."""
+ # FIXME: Order should be a parameter
+ # return iterator over Nodes instances
+ type_ = self.schema.node(node_type)
+ for guid in self.__get(node_type, query):
+ yield _nodes.Nodes(self._backend, self._ac, type_, {guid})
def all(self, node_type: URI) -> _nodes.Nodes:
"""Return all instances of type *node_type*."""
+ type_ = self.schema.node(node_type)
+ return _nodes.Nodes(self._backend, self._ac, type_, self.__get(node_type, None))
+
+ def __get(
+ self,
+ node_type: URI,
+ query: typing.Optional[ast.filter.FilterExpression],
+ ) -> typing.Iterator[URI]:
+ """Build and execute a get query."""
# get node type
type_ = self.schema.node(node_type)
- guids = self._backend.get(type_, None) # no need to materialize
- return _nodes.Nodes(self._backend, self._user, type_, guids)
-
+ # resolve Nodes instances
+ query = resolve.Filter(self._backend.schema).resolve(type_, query)
+ # add access controls to query
+ query = self._ac.filter_read(type_, query)
+ # validate query
+ if query is not None:
+ validate.Filter(self._backend.schema).validate(type_, query)
+ # query the backend and return the (non-materialized) result
+ return self._backend.get(type_, query)
## EOF ##
diff --git a/bsfs/graph/nodes.py b/bsfs/graph/nodes.py
index bc71a32..47b0217 100644
--- a/bsfs/graph/nodes.py
+++ b/bsfs/graph/nodes.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
from collections import abc
import time
@@ -30,15 +25,17 @@ __all__: typing.Sequence[str] = (
## code ##
class Nodes():
- """
+ """Container for graph nodes, provides operations on nodes.
+
+ NOTE: Should not be created directly but only via `bsfs.graph.Graph`.
NOTE: guids may or may not exist. This is not verified as nodes are created on demand.
"""
# triple store backend.
_backend: TripleStoreBase
- # user uri.
- _user: URI
+ # access controls.
+ _ac: ac.AccessControlBase
# node type.
_node_type: bsc.Node
@@ -49,31 +46,29 @@ class Nodes():
def __init__(
self,
backend: TripleStoreBase,
- user: URI,
+ access_control: ac.AccessControlBase,
node_type: bsc.Node,
guids: typing.Iterable[URI],
):
# set main members
self._backend = backend
- self._user = user
+ self._ac = access_control
self._node_type = node_type
- self._guids = set(guids)
- # create helper instances
- # FIXME: Assumes that the schema does not change while the instance is in use!
- self._ac = ac.NullAC(self._backend, self._user)
+ # convert to URI since this is not guaranteed by Graph
+ self._guids = {URI(guid) for guid in guids}
def __eq__(self, other: typing.Any) -> bool:
return isinstance(other, Nodes) \
and self._backend == other._backend \
- and self._user == other._user \
+ and self._ac == other._ac \
and self._node_type == other._node_type \
and self._guids == other._guids
def __hash__(self) -> int:
- return hash((type(self), self._backend, self._user, self._node_type, tuple(sorted(self._guids))))
+ return hash((type(self), self._backend, self._ac, self._node_type, tuple(sorted(self._guids))))
def __repr__(self) -> str:
- return f'{typename(self)}({self._backend}, {self._user}, {self._node_type}, {self._guids})'
+ return f'{typename(self)}({self._backend}, {self._ac}, {self._node_type}, {self._guids})'
def __str__(self) -> str:
return f'{typename(self)}({self._node_type}, {self._guids})'
@@ -94,44 +89,44 @@ class Nodes():
return self._backend.schema
def __add__(self, other: typing.Any) -> 'Nodes':
- """Concatenate guids. Backend, user, and node type must match."""
+ """Concatenate guids. Backend, AC, and node type must match."""
if not isinstance(other, type(self)):
return NotImplemented
if self._backend != other._backend:
raise ValueError(other)
- if self._user != other._user:
+ if self._ac != other._ac:
raise ValueError(other)
if self.node_type != other.node_type:
raise ValueError(other)
- return Nodes(self._backend, self._user, self.node_type, self._guids | other._guids)
+ return Nodes(self._backend, self._ac, self.node_type, self._guids | other._guids)
def __or__(self, other: typing.Any) -> 'Nodes':
- """Concatenate guids. Backend, user, and node type must match."""
+ """Concatenate guids. Backend, AC, and node type must match."""
return self.__add__(other)
def __sub__(self, other: typing.Any) -> 'Nodes':
- """Subtract guids. Backend, user, and node type must match."""
+ """Subtract guids. Backend, AC, and node type must match."""
if not isinstance(other, type(self)):
return NotImplemented
if self._backend != other._backend:
raise ValueError(other)
- if self._user != other._user:
+ if self._ac != other._ac:
raise ValueError(other)
if self.node_type != other.node_type:
raise ValueError(other)
- return Nodes(self._backend, self._user, self.node_type, self._guids - other._guids)
+ return Nodes(self._backend, self._ac, self.node_type, self._guids - other._guids)
def __and__(self, other: typing.Any) -> 'Nodes':
- """Intersect guids. Backend, user, and node type must match."""
+ """Intersect guids. Backend, AC, and node type must match."""
if not isinstance(other, type(self)):
return NotImplemented
if self._backend != other._backend:
raise ValueError(other)
- if self._user != other._user:
+ if self._ac != other._ac:
raise ValueError(other)
if self.node_type != other.node_type:
raise ValueError(other)
- return Nodes(self._backend, self._user, self.node_type, self._guids & other._guids)
+ return Nodes(self._backend, self._ac, self.node_type, self._guids & other._guids)
def __len__(self) -> int:
"""Return the number of guids."""
@@ -140,7 +135,7 @@ class Nodes():
def __iter__(self) -> typing.Iterator['Nodes']:
"""Iterate over individual guids. Returns `Nodes` instances."""
return iter(
- Nodes(self._backend, self._user, self.node_type, {guid})
+ Nodes(self._backend, self._ac, self.node_type, {guid})
for guid in self._guids
)
@@ -175,7 +170,7 @@ class Nodes():
self._backend.commit()
except (
- errors.PermissionDeniedError, # tried to set a protected predicate (ns.bsm.t_created)
+ errors.PermissionDeniedError, # tried to set a protected predicate
errors.ConsistencyError, # node types are not in the schema or don't match the predicate
errors.InstanceError, # guids/values don't have the correct type
TypeError, # value is supposed to be a Nodes instance
@@ -250,32 +245,38 @@ class Nodes():
# add access controls to fetch
fetch = self._ac.fetch_read(self.node_type, fetch)
- # compose filter ast
- filter = ast.filter.IsIn(self.guids) # pylint: disable=redefined-builtin
- # add access controls to filter
- filter = self._ac.filter_read(self.node_type, filter)
-
- # validate queries
- validate.Filter(self._backend.schema)(self.node_type, filter)
- validate.Fetch(self._backend.schema)(self.node_type, fetch)
-
- # process results, convert if need be
- def triple_iter():
- # query the backend
- triples = self._backend.fetch(self.node_type, filter, fetch)
- # process triples
- for root, name, raw in triples:
- # get node
- node = Nodes(self._backend, self._user, self.node_type, {root})
- # get path
- path, tail = name2path[name]
- # covert raw to value
- if isinstance(tail.range, bsc.Node):
- value = Nodes(self._backend, self._user, tail.range, {raw})
- else:
- value = raw
- # emit triple
- yield node, path, value
+ if len(self._guids) == 0:
+ # shortcut: no need to query; no triples
+ # FIXME: if the Fetch query can given by the user, we might want to check its validity
+ def triple_iter():
+ return []
+ else:
+ # compose filter ast
+ filter = ast.filter.IsIn(self.guids) # pylint: disable=redefined-builtin
+ # add access controls to filter
+ filter = self._ac.filter_read(self.node_type, filter) # type: ignore [assignment]
+
+ # validate queries
+ validate.Filter(self._backend.schema).validate(self.node_type, filter)
+ validate.Fetch(self._backend.schema).validate(self.node_type, fetch)
+
+ # process results, convert if need be
+ def triple_iter():
+ # query the backend
+ triples = self._backend.fetch(self.node_type, filter, fetch)
+ # process triples
+ for root, name, raw in triples:
+ # get node
+ node = Nodes(self._backend, self._ac, self.node_type, {root})
+ # get path
+ path, tail = name2path[name]
+ # covert raw to value
+ if isinstance(tail.range, bsc.Node):
+ value = Nodes(self._backend, self._ac, tail.range, {raw})
+ else:
+ value = raw
+ # emit triple
+ yield node, path, value
# simplify by default
view_kwargs['node'] = view_kwargs.get('node', len(self._guids) != 1)
@@ -393,7 +394,7 @@ class Nodes():
self._backend.create(node_type, missing)
# add bookkeeping triples
self._backend.set(node_type, missing,
- self._backend.schema.predicate(ns.bsm.t_created), [time.time()])
+ self._backend.schema.predicate(ns.bsn.t_created), [time.time()])
# add permission triples
self._ac.create(node_type, missing)
# return available nodes
diff --git a/bsfs/graph/resolve.py b/bsfs/graph/resolve.py
index 4677401..a58eb67 100644
--- a/bsfs/graph/resolve.py
+++ b/bsfs/graph/resolve.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
@@ -32,16 +27,30 @@ class Filter():
input: Any(ns.bse.tag, Is(Nodes(...)))
output: Any(ns.bse.tag, Or(Is(...), Is(...), ...)))
- >>> tags = graph.node(ns.bsfs.Tag, 'http://example.com/me/tag#1234')
- >>> graph.get(ns.bsfs.Entity, ast.filter.Any(ns.bse.tag, ast.filter.Is(tags)))
+ >>> tags = graph.node(ns.bsn.Tag, 'http://example.com/me/tag#1234')
+ >>> graph.get(ns.bsn.Entity, ast.filter.Any(ns.bse.tag, ast.filter.Is(tags)))
"""
def __init__(self, schema):
self.schema = schema
- def __call__(self, root_type: bsc.Node, node: ast.filter.FilterExpression):
- # FIXME: node can be None!
+ def __call__(
+ self,
+ root_type: bsc.Node,
+ node: typing.Optional[ast.filter.FilterExpression],
+ ):
+ """Alias for `Resolve.resolve`."""
+ return self.resolve(root_type, node)
+
+ def resolve(
+ self,
+ root_type: bsc.Node,
+ node: typing.Optional[ast.filter.FilterExpression],
+ ):
+ """Resolve Nodes instances of a *node* query starting at *root_type*."""
+ if node is None:
+ return None
return self._parse_filter_expression(root_type, node)
def _parse_filter_expression(
diff --git a/bsfs/graph/result.py b/bsfs/graph/result.py
index 31822f1..0fcbb13 100644
--- a/bsfs/graph/result.py
+++ b/bsfs/graph/result.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
from collections import defaultdict
import typing
diff --git a/bsfs/graph/schema.nt b/bsfs/graph/schema.nt
index f619746..37bba5e 100644
--- a/bsfs/graph/schema.nt
+++ b/bsfs/graph/schema.nt
@@ -4,16 +4,17 @@ prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
# bsfs prefixes
-prefix bsfs: <http://bsfs.ai/schema/>
-prefix bsm: <http://bsfs.ai/schema/Meta#>
+prefix bsfs: <https://schema.bsfs.io/core/>
+prefix bsl: <https://schema.bsfs.io/core/Literal/>
+prefix bsn: <https://schema.bsfs.io/core/Node#>
# literals
-bsfs:Number rdfs:subClassOf bsfs:Literal .
-xsd:integer rdfs:subClassOf bsfs:Number .
+bsl:Number rdfs:subClassOf bsfs:Literal .
+xsd:float rdfs:subClassOf bsl:Number .
# predicates
-bsm:t_created rdfs:subClassOf bsfs:Predicate ;
+bsn:t_created rdfs:subClassOf bsfs:Predicate ;
rdfs:domain bsfs:Node ;
- rdfs:range xsd:integer ;
+ rdfs:range xsd:float ;
bsfs:unique "true"^^xsd:boolean .
diff --git a/bsfs/graph/walk.py b/bsfs/graph/walk.py
index 1b1cfa0..6415c9b 100644
--- a/bsfs/graph/walk.py
+++ b/bsfs/graph/walk.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
from collections import abc
import typing
diff --git a/bsfs/namespace/__init__.py b/bsfs/namespace/__init__.py
index 98d472f..76f39a2 100644
--- a/bsfs/namespace/__init__.py
+++ b/bsfs/namespace/__init__.py
@@ -1,19 +1,13 @@
-"""
-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 . import predefined as ns
-from .namespace import ClosedNamespace, Namespace
+from .namespace import Namespace
# exports
__all__: typing.Sequence[str] = (
- 'ClosedNamespace',
'Namespace',
'ns',
)
diff --git a/bsfs/namespace/namespace.py b/bsfs/namespace/namespace.py
index 1d443c1..b388f53 100644
--- a/bsfs/namespace/namespace.py
+++ b/bsfs/namespace/namespace.py
@@ -1,104 +1,54 @@
-"""
-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.utils import URI, typename
+from bsfs.utils import URI
# exports
__all__: typing.Sequence[str] = (
- 'ClosedNamespace',
'Namespace',
+ 'FinalNamespace',
)
## code ##
-class Namespace():
- """A namespace consists of a common prefix that is used in a set of URIs.
- Note that the prefix must include the separator between
- path and fragment (typically a '#' or a '/').
- """
-
- # namespace prefix.
- prefix: URI
-
- # fragment separator.
- fsep: str
-
- # path separator.
- psep: str
-
- def __init__(self, prefix: URI, fsep: str = '#', psep: str = '/'):
- # ensure prefix type
- prefix = URI(prefix)
- # truncate fragment separator
- while prefix.endswith(fsep):
- prefix = URI(prefix[:-1])
- # truncate path separator
- while prefix.endswith(psep):
- prefix = URI(prefix[:-1])
- # store members
- self.prefix = prefix
- self.fsep = fsep
- self.psep = psep
-
- def __eq__(self, other: typing.Any) -> bool:
- return isinstance(other, type(self)) \
- and self.prefix == other.prefix \
- and self.fsep == other.fsep \
- and self.psep == other.psep
+class Namespace(URI):
+ """The Namespace allows you to incrementally append path segments to an URI.
- def __hash__(self) -> int:
- return hash((type(self), self.prefix, self.fsep, self.psep))
+ Segments are separated by `Namespace.sep` ('/').
+ The `__call__` method signals that the URI is complete until the query part.
- def __str__(self) -> str:
- return str(self.prefix)
-
- def __repr__(self) -> str:
- return f'{typename(self)}({self.prefix}, {self.fsep}, {self.psep})'
-
- def __getattr__(self, fragment: str) -> URI:
- """Return prefix + fragment."""
- return URI(self.prefix + self.fsep + fragment)
-
- def __getitem__(self, fragment: str) -> URI:
- """Alias for getattr(self, fragment)."""
- return self.__getattr__(fragment)
+ """
- def __add__(self, value: typing.Any) -> 'Namespace':
- """Concatenate another namespace to this one."""
- if not isinstance(value, str):
- return NotImplemented
- return Namespace(self.prefix + self.psep + value, self.fsep, self.psep)
+ # path separator
+ sep: str = '/'
+ def __getattr__(self, query: str) -> 'Namespace':
+ """Append the *query* to the current value and return as Namespace."""
+ return Namespace(self + self.sep + query)
-class ClosedNamespace(Namespace):
- """Namespace that covers a restricted set of URIs."""
+ def __call__(self, sep: str = '#') -> 'FinalNamespace':
+ """Finalize the namespace."""
+ return FinalNamespace(self, sep)
- # set of permissible fragments.
- fragments: typing.Set[str]
- def __init__(self, prefix: URI, *args: str, fsep: str = '#', psep: str = '/'):
- super().__init__(prefix, fsep, psep)
- self.fragments = set(args)
+# FIXME: Integrate FinalNamespace into Namespace? Do we need to have both?
+class FinalNamespace(URI):
+ """The FinalNamespace allows you to append a fragment to an URI."""
- def __eq__(self, other: typing.Any) -> bool:
- return super().__eq__(other) and self.fragments == other.fragments
+ # fragment separator
+ sep: str
- def __hash__(self) -> int:
- return hash((type(self), self.prefix, tuple(sorted(self.fragments))))
+ def __new__(cls, value: str, sep: str = '#'):
+ inst = URI.__new__(cls, value)
+ inst.sep = sep
+ return inst
def __getattr__(self, fragment: str) -> URI:
- """Return prefix + fragment or raise a KeyError if the fragment is not part of this namespace."""
- if fragment not in self.fragments:
- raise KeyError(f'{fragment} is not a valid fragment of namespace {self.prefix}')
- return super().__getattr__(fragment)
+ """Append the *fragment* to the current value and return as URI."""
+ return URI(self + self.sep + fragment)
## EOF ##
diff --git a/bsfs/namespace/predefined.py b/bsfs/namespace/predefined.py
index cd48a46..8b60d39 100644
--- a/bsfs/namespace/predefined.py
+++ b/bsfs/namespace/predefined.py
@@ -1,35 +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.utils import URI
-
# inner-module imports
-from . import namespace
+from .namespace import Namespace, FinalNamespace
# essential bsfs namespaces
-bsfs: namespace.Namespace = namespace.Namespace(URI('http://bsfs.ai/schema'), fsep='/')
-
+bsfs = Namespace('https://schema.bsfs.io/core')
# additional bsfs namespaces
-bse: namespace.Namespace = namespace.Namespace(URI('http://bsfs.ai/schema/Entity'))
-bsm: namespace.Namespace = namespace.Namespace(URI('http://bsfs.ai/schema/Meta'))
+bsd = bsfs.distance()
+bsl = bsfs.Literal
+bsn = bsfs.Node()
# generic namespaces
-rdf: namespace.Namespace = namespace.Namespace(URI('http://www.w3.org/1999/02/22-rdf-syntax-ns'))
-rdfs: namespace.Namespace = namespace.Namespace(URI('http://www.w3.org/2000/01/rdf-schema'))
-schema: namespace.Namespace = namespace.Namespace(URI('http://schema.org'), fsep='/')
-xsd: namespace.Namespace = namespace.Namespace(URI('http://www.w3.org/2001/XMLSchema'))
+rdf = FinalNamespace('http://www.w3.org/1999/02/22-rdf-syntax-ns')
+rdfs = FinalNamespace('http://www.w3.org/2000/01/rdf-schema')
+xsd = FinalNamespace('http://www.w3.org/2001/XMLSchema')
+schema = FinalNamespace('http://schema.org', sep='/')
+# exports
__all__: typing.Sequence[str] = (
- 'bse',
+ 'bsd',
'bsfs',
- 'bsm',
+ 'bsl',
+ 'bsn',
'rdf',
'rdfs',
'schema',
diff --git a/bsfs/query/__init__.py b/bsfs/query/__init__.py
index 21c7389..58ff03a 100644
--- a/bsfs/query/__init__.py
+++ b/bsfs/query/__init__.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/query/ast/__init__.py b/bsfs/query/ast/__init__.py
index 66b097d..bceaac0 100644
--- a/bsfs/query/ast/__init__.py
+++ b/bsfs/query/ast/__init__.py
@@ -6,9 +6,6 @@ Classes beginning with an underscore (_) represent internal type hierarchies
and should not be used for parsing. Note that the AST structures do not
(and cannot) check semantic validity or consistency with a given schema.
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
"""
# imports
import typing
diff --git a/bsfs/query/ast/fetch.py b/bsfs/query/ast/fetch.py
index d653a8a..66d94e1 100644
--- a/bsfs/query/ast/fetch.py
+++ b/bsfs/query/ast/fetch.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
from collections import abc
import typing
diff --git a/bsfs/query/ast/filter_.py b/bsfs/query/ast/filter_.py
index b29d89e..610fdb4 100644
--- a/bsfs/query/ast/filter_.py
+++ b/bsfs/query/ast/filter_.py
@@ -10,7 +10,8 @@ For example, consider the following AST:
>>> Any(ns.bse.collection,
... And(
... Equals('hello'),
-... Any(ns.bsm.guid, Any(ns.bsm.guid, Equals('hello'))),
+... Is('hello world'),
+... Any(ns.bse.tag, Equals('world')),
... Any(ns.bst.label, Equals('world')),
... All(ns.bst.label, Not(Equals('world'))),
... )
@@ -22,9 +23,6 @@ This AST has multiple issues that are not verified upon its creation:
* Conditions exclude each other
* The predicate along the branch have incompatible domains and ranges.
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
"""
# imports
from collections import abc
diff --git a/bsfs/query/matcher.py b/bsfs/query/matcher.py
index a910756..17c9c8e 100644
--- a/bsfs/query/matcher.py
+++ b/bsfs/query/matcher.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
from collections import defaultdict
from itertools import product
@@ -220,8 +215,8 @@ class Filter():
two following queries are semantically identical, but structurally different,
and would therefore not match:
- >>> ast.filter.OneOf(ast.filter.Predicate(ns.bse.filename))
- >>> ast.filter.Predicate(ns.bse.filename)
+ >>> ast.filter.OneOf(ast.filter.Predicate(ns.bse.name))
+ >>> ast.filter.Predicate(ns.bse.name)
"""
diff --git a/bsfs/query/validator.py b/bsfs/query/validator.py
index f0aa795..10ca492 100644
--- a/bsfs/query/validator.py
+++ b/bsfs/query/validator.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
@@ -41,7 +36,11 @@ class Filter():
def __init__(self, schema: bsc.Schema):
self.schema = schema
- def __call__(self, root_type: bsc.Node, query: ast.filter.FilterExpression):
+ def __call__(self, root_type: bsc.Node, query: ast.filter.FilterExpression) -> bool:
+ """Alias for `Filter.validate`."""
+ return self.validate(root_type, query)
+
+ def validate(self, root_type: bsc.Node, query: ast.filter.FilterExpression) -> bool:
"""Validate a filter *query*, assuming the subject having *root_type*.
Raises a `bsfs.utils.errors.ConsistencyError` if the query violates the schema.
@@ -178,7 +177,7 @@ class Filter():
if not type_ <= dom:
raise errors.ConsistencyError(f'expected type {dom}, found {type_}')
# node.count is a numerical expression
- self._parse_filter_expression(self.schema.literal(ns.bsfs.Number), node.count)
+ self._parse_filter_expression(self.schema.literal(ns.bsl.Number), node.count)
def _distance(self, type_: bsc.Vertex, node: ast.filter.Distance):
# type is a Literal
@@ -219,7 +218,7 @@ class Filter():
if type_ not in self.schema.literals():
raise errors.ConsistencyError(f'literal {type_} is not in the schema')
# type must be a numerical
- if not type_ <= self.schema.literal(ns.bsfs.Number):
+ if not type_ <= self.schema.literal(ns.bsl.Number):
raise errors.ConsistencyError(f'expected a number type, found {type_}')
# FIXME: Check if node.value corresponds to type_
@@ -242,7 +241,11 @@ class Fetch():
def __init__(self, schema: bsc.Schema):
self.schema = schema
- def __call__(self, root_type: bsc.Node, query: ast.fetch.FetchExpression):
+ def __call__(self, root_type: bsc.Node, query: ast.fetch.FetchExpression) -> bool:
+ """Alias for `Fetch.validate`."""
+ return self.validate(root_type, query)
+
+ def validate(self, root_type: bsc.Node, query: ast.fetch.FetchExpression) -> bool:
"""Validate a fetch *query*, assuming the subject having *root_type*.
Raises a `bsfs.utils.errors.ConsistencyError` if the query violates the schema.
diff --git a/bsfs/schema/__init__.py b/bsfs/schema/__init__.py
index f53512e..ca2e0cd 100644
--- a/bsfs/schema/__init__.py
+++ b/bsfs/schema/__init__.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/schema/schema.py b/bsfs/schema/schema.py
index 0de4203..c104436 100644
--- a/bsfs/schema/schema.py
+++ b/bsfs/schema/schema.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
from collections import abc, namedtuple
import typing
diff --git a/bsfs/schema/serialize.py b/bsfs/schema/serialize.py
index acc009a..ea8b2f4 100644
--- a/bsfs/schema/serialize.py
+++ b/bsfs/schema/serialize.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# standard imports
import itertools
import typing
@@ -246,13 +241,14 @@ def to_string(schema_inst: schema.Schema, fmt: str = 'turtle') -> str:
graph.add(triple)
# add known namespaces for readability
# FIXME: more generically?
- graph.bind('bse', rdflib.URIRef(ns.bse['']))
- graph.bind('bsfs', rdflib.URIRef(ns.bsfs['']))
- graph.bind('bsm', rdflib.URIRef(ns.bsm['']))
- graph.bind('rdf', rdflib.URIRef(ns.rdf['']))
- graph.bind('rdfs', rdflib.URIRef(ns.rdfs['']))
- graph.bind('schema', rdflib.URIRef(ns.schema['']))
- graph.bind('xsd', rdflib.URIRef(ns.xsd['']))
+ graph.bind('bsfs', rdflib.URIRef(ns.bsfs + '/'))
+ graph.bind('bsl', rdflib.URIRef(ns.bsl + '/'))
+ graph.bind('bsn', rdflib.URIRef(ns.bsn + '#'))
+ graph.bind('bse', rdflib.URIRef(ns.bsfs.Entity() + '#'))
+ graph.bind('rdf', rdflib.URIRef(ns.rdf))
+ graph.bind('rdfs', rdflib.URIRef(ns.rdfs))
+ graph.bind('schema', rdflib.URIRef(ns.schema))
+ graph.bind('xsd', rdflib.URIRef(ns.xsd))
# serialize to turtle
return graph.serialize(format=fmt)
diff --git a/bsfs/schema/types.py b/bsfs/schema/types.py
index 12e7e94..5834df8 100644
--- a/bsfs/schema/types.py
+++ b/bsfs/schema/types.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
@@ -103,7 +98,7 @@ class _Type():
parent: typing.Optional['_Type'] = None,
**annotations: typing.Any,
):
- self.uri = uri
+ self.uri = URI(uri)
self.parent = parent
self.annotations = annotations
@@ -381,31 +376,31 @@ ROOT_LITERAL = Literal(
)
ROOT_BLOB = Literal(
- uri=ns.bsfs.BinaryBlob,
+ uri=ns.bsl.BinaryBlob,
parent=ROOT_LITERAL,
)
ROOT_NUMBER = Literal(
- uri=ns.bsfs.Number,
+ uri=ns.bsl.Number,
parent=ROOT_LITERAL,
)
ROOT_TIME = Literal(
- uri=ns.bsfs.Time,
+ uri=ns.bsl.Time,
parent=ROOT_LITERAL,
)
ROOT_ARRAY = Literal(
- uri=ns.bsfs.Array,
+ uri=ns.bsl.Array,
parent=ROOT_LITERAL,
)
ROOT_FEATURE = Feature(
- uri=ns.bsfs.Feature,
+ uri=ns.bsl.Array.Feature,
parent=ROOT_ARRAY,
dimension=1,
- dtype=ns.bsfs.f16,
- distance=ns.bsfs.euclidean,
+ dtype=ns.bsfs.dtype().f16,
+ distance=ns.bsd.euclidean,
)
# essential predicates
diff --git a/bsfs/triple_store/__init__.py b/bsfs/triple_store/__init__.py
index fb5a8a9..79a2887 100644
--- a/bsfs/triple_store/__init__.py
+++ b/bsfs/triple_store/__init__.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/triple_store/base.py b/bsfs/triple_store/base.py
index 1baa63b..58b5670 100644
--- a/bsfs/triple_store/base.py
+++ b/bsfs/triple_store/base.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import abc
import typing
diff --git a/bsfs/triple_store/sparql/__init__.py b/bsfs/triple_store/sparql/__init__.py
index 285334a..cfa2732 100644
--- a/bsfs/triple_store/sparql/__init__.py
+++ b/bsfs/triple_store/sparql/__init__.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/triple_store/sparql/distance.py b/bsfs/triple_store/sparql/distance.py
index 2f5387a..2c2f355 100644
--- a/bsfs/triple_store/sparql/distance.py
+++ b/bsfs/triple_store/sparql/distance.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# standard imports
import typing
@@ -48,9 +43,9 @@ def manhatten(fst, snd) -> float:
# Known distance functions.
DISTANCE_FU = {
- ns.bsfs.euclidean: euclid,
- ns.bsfs.cosine: cosine,
- ns.bsfs.manhatten: manhatten,
+ ns.bsd.euclidean: euclid,
+ ns.bsd.cosine: cosine,
+ ns.bsd.manhatten: manhatten,
}
## EOF ##
diff --git a/bsfs/triple_store/sparql/parse_fetch.py b/bsfs/triple_store/sparql/parse_fetch.py
index 20d4e74..fab8173 100644
--- a/bsfs/triple_store/sparql/parse_fetch.py
+++ b/bsfs/triple_store/sparql/parse_fetch.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# standard imports
import typing
diff --git a/bsfs/triple_store/sparql/parse_filter.py b/bsfs/triple_store/sparql/parse_filter.py
index dca0aea..2f5a25b 100644
--- a/bsfs/triple_store/sparql/parse_filter.py
+++ b/bsfs/triple_store/sparql/parse_filter.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import operator
import typing
@@ -156,16 +151,16 @@ class Filter():
raise errors.BackendError(f'the range of predicate {pred} is undefined')
dom, rng = pred.domain, pred.range
# encapsulate predicate uri
- puri = f'<{puri}>' # type: ignore [assignment] # variable re-use confuses mypy
+ uri_str = f'<{puri}>'
# apply reverse flag
if node.reverse:
- puri = URI('^' + puri)
+ uri_str = '^' + uri_str
dom, rng = rng, dom # type: ignore [assignment] # variable re-use confuses mypy
# check path consistency
if not node_type <= dom:
raise errors.ConsistencyError(f'expected type {dom} or subtype thereof, found {node_type}')
# return predicate URI and next node type
- return puri, rng
+ return uri_str, rng
def _any(self, node_type: bsc.Vertex, node: ast.filter.Any, head: str) -> str:
"""
@@ -272,7 +267,7 @@ class Filter():
"""
if not isinstance(node_type, bsc.Node):
raise errors.BackendError(f'expected Node, found {node_type}')
- return f'VALUES {head} {{ <{node.value}> }}'
+ return f'VALUES {head} {{ <{URI(node.value)}> }}'
def _equals(self, node_type: bsc.Vertex, node: ast.filter.Equals, head: str) -> str:
"""
diff --git a/bsfs/triple_store/sparql/sparql.py b/bsfs/triple_store/sparql/sparql.py
index dbf9d45..99e67d6 100644
--- a/bsfs/triple_store/sparql/sparql.py
+++ b/bsfs/triple_store/sparql/sparql.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import base64
import itertools
@@ -33,7 +28,7 @@ __all__: typing.Sequence[str] = (
## code ##
-rdflib.term.bind(ns.bsfs.BinaryBlob, bytes, constructor=base64.b64decode)
+rdflib.term.bind(ns.bsl.BinaryBlob, bytes, constructor=base64.b64decode)
class _Transaction():
"""Lightweight rdflib transactions for in-memory databases."""
@@ -289,7 +284,7 @@ class SparqlStore(base.TripleStoreBase):
raise errors.ConsistencyError(f'{node_type} is not defined in the schema')
# check and create guids
for guid in guids:
- subject = rdflib.URIRef(guid)
+ subject = rdflib.URIRef(URI(guid))
# check node existence
if (subject, rdflib.RDF.type, None) in self._graph:
# FIXME: node exists and may have a different type! ignore? raise? report?
@@ -328,7 +323,7 @@ class SparqlStore(base.TripleStoreBase):
raise errors.InstanceError(inconsistent)
# check guids
# FIXME: Fail or skip inexistent nodes?
- guids = set(guids)
+ guids = {URI(guid) for guid in guids}
inconsistent = {guid for guid in guids if not self._has_type(guid, node_type)}
if len(inconsistent) > 0:
raise errors.InstanceError(inconsistent)
@@ -340,8 +335,8 @@ class SparqlStore(base.TripleStoreBase):
# convert value
if isinstance(predicate.range, bsc.Literal):
dtype = rdflib.URIRef(predicate.range.uri)
- if predicate.range <= self.schema.literal(ns.bsfs.BinaryBlob):
- dtype = rdflib.URIRef(ns.bsfs.BinaryBlob)
+ if predicate.range <= self.schema.literal(ns.bsl.BinaryBlob):
+ dtype = rdflib.URIRef(ns.bsl.BinaryBlob)
value = base64.b64encode(value)
value = rdflib.Literal(value, datatype=dtype)
elif isinstance(predicate.range, bsc.Node):
diff --git a/bsfs/triple_store/sparql/utils.py b/bsfs/triple_store/sparql/utils.py
index deca4d8..38062c2 100644
--- a/bsfs/triple_store/sparql/utils.py
+++ b/bsfs/triple_store/sparql/utils.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# standard imports
import typing
@@ -127,11 +122,12 @@ class Query():
"""Return an executable sparql query."""
select = ' '.join(f'({head} as ?{name})' for head, name in self.select)
return f'''
- SELECT {self.root_head} {select}
+ SELECT DISTINCT {self.root_head} {select}
WHERE {{
{self.root_head} <{ns.rdf.type}>/<{ns.rdfs.subClassOf}>* <{self.root_type}> .
{self.where}
}}
+ ORDER BY str({self.root_head})
'''
def __call__(self, graph: rdflib.Graph) -> rdflib.query.Result:
diff --git a/bsfs/utils/__init__.py b/bsfs/utils/__init__.py
index 6737cef..d497645 100644
--- a/bsfs/utils/__init__.py
+++ b/bsfs/utils/__init__.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/utils/commons.py b/bsfs/utils/commons.py
index e9f0b7f..a7092ae 100644
--- a/bsfs/utils/commons.py
+++ b/bsfs/utils/commons.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
from collections import abc
import typing
diff --git a/bsfs/utils/errors.py b/bsfs/utils/errors.py
index 6ae6484..b82e6e2 100644
--- a/bsfs/utils/errors.py
+++ b/bsfs/utils/errors.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import typing
diff --git a/bsfs/utils/uri.py b/bsfs/utils/uri.py
index 84854a4..5755a6e 100644
--- a/bsfs/utils/uri.py
+++ b/bsfs/utils/uri.py
@@ -1,14 +1,11 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
import re
import typing
# constants
+RX_CHARS = re.compile(r'[<>" {}|\\^]')
+
RX_URI = re.compile(r'''
^
(?:(?P<scheme>[^:/?#]+):)? # scheme, ://-delimited
@@ -82,6 +79,9 @@ class URI(str):
no claim about the validity of an URI!
"""
+ # check characters
+ if RX_CHARS.search(query) is not None:
+ return False
# check uri
parts = RX_URI.match(query)
if parts is not None:
@@ -232,9 +232,6 @@ class URI(str):
# overload formatting methods
- def format(self, *args, **kwargs) -> 'URI':
- return URI(super().format(*args, **kwargs))
-
def __mod__(self, *args) -> 'URI':
return URI(super().__mod__(*args))
diff --git a/bsfs/utils/uuid.py b/bsfs/utils/uuid.py
index 70e1656..ad7fc1c 100644
--- a/bsfs/utils/uuid.py
+++ b/bsfs/utils/uuid.py
@@ -1,9 +1,4 @@
-"""
-Part of the BlackStar filesystem (bsfs) module.
-A copy of the license is provided with the project.
-Author: Matthias Baumgartner, 2022
-"""
# imports
from collections import abc
import hashlib