aboutsummaryrefslogtreecommitdiffstats
path: root/bsfs
diff options
context:
space:
mode:
authorMatthias Baumgartner <dev@igsor.net>2023-03-04 13:31:11 +0100
committerMatthias Baumgartner <dev@igsor.net>2023-03-04 13:44:26 +0100
commit4fead04055be4967d9ea3b24ff61fe37a93108dd (patch)
tree40fb5ea2874466cae1b3fb6ad84d1d133992bf34 /bsfs
parent2c6c23f85e7f2123c508f9ff8a4aa776948bb589 (diff)
downloadbsfs-4fead04055be4967d9ea3b24ff61fe37a93108dd.tar.gz
bsfs-4fead04055be4967d9ea3b24ff61fe37a93108dd.tar.bz2
bsfs-4fead04055be4967d9ea3b24ff61fe37a93108dd.zip
namespace refactoring and cleanup
Diffstat (limited to 'bsfs')
-rw-r--r--bsfs/graph/ac/null.py2
-rw-r--r--bsfs/graph/nodes.py4
-rw-r--r--bsfs/graph/resolve.py4
-rw-r--r--bsfs/graph/schema.nt11
-rw-r--r--bsfs/namespace/__init__.py3
-rw-r--r--bsfs/namespace/namespace.py97
-rw-r--r--bsfs/namespace/predefined.py27
-rw-r--r--bsfs/query/ast/filter_.py3
-rw-r--r--bsfs/query/matcher.py4
-rw-r--r--bsfs/query/validator.py4
-rw-r--r--bsfs/schema/serialize.py15
-rw-r--r--bsfs/schema/types.py14
-rw-r--r--bsfs/triple_store/sparql/distance.py6
-rw-r--r--bsfs/triple_store/sparql/sparql.py6
14 files changed, 78 insertions, 122 deletions
diff --git a/bsfs/graph/ac/null.py b/bsfs/graph/ac/null.py
index 3a391aa..c9ec7d0 100644
--- a/bsfs/graph/ac/null.py
+++ b/bsfs/graph/ac/null.py
@@ -24,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."""
diff --git a/bsfs/graph/nodes.py b/bsfs/graph/nodes.py
index 74f4c4f..47b0217 100644
--- a/bsfs/graph/nodes.py
+++ b/bsfs/graph/nodes.py
@@ -170,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
@@ -394,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 95dcfc1..a58eb67 100644
--- a/bsfs/graph/resolve.py
+++ b/bsfs/graph/resolve.py
@@ -27,8 +27,8 @@ 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)))
"""
diff --git a/bsfs/graph/schema.nt b/bsfs/graph/schema.nt
index cba5e80..37bba5e 100644
--- a/bsfs/graph/schema.nt
+++ b/bsfs/graph/schema.nt
@@ -4,15 +4,16 @@ 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:float 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:float ;
bsfs:unique "true"^^xsd:boolean .
diff --git a/bsfs/namespace/__init__.py b/bsfs/namespace/__init__.py
index 1784808..76f39a2 100644
--- a/bsfs/namespace/__init__.py
+++ b/bsfs/namespace/__init__.py
@@ -4,11 +4,10 @@ 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 0a62b78..b388f53 100644
--- a/bsfs/namespace/namespace.py
+++ b/bsfs/namespace/namespace.py
@@ -3,97 +3,52 @@
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 15f12ac..8b60d39 100644
--- a/bsfs/namespace/predefined.py
+++ b/bsfs/namespace/predefined.py
@@ -2,29 +2,28 @@
# 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/ast/filter_.py b/bsfs/query/ast/filter_.py
index 56c982e..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'))),
... )
diff --git a/bsfs/query/matcher.py b/bsfs/query/matcher.py
index 5f3b07e..17c9c8e 100644
--- a/bsfs/query/matcher.py
+++ b/bsfs/query/matcher.py
@@ -215,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 1ce44e9..10ca492 100644
--- a/bsfs/query/validator.py
+++ b/bsfs/query/validator.py
@@ -177,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
@@ -218,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_
diff --git a/bsfs/schema/serialize.py b/bsfs/schema/serialize.py
index b05b289..ea8b2f4 100644
--- a/bsfs/schema/serialize.py
+++ b/bsfs/schema/serialize.py
@@ -241,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 104580d..5834df8 100644
--- a/bsfs/schema/types.py
+++ b/bsfs/schema/types.py
@@ -376,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/sparql/distance.py b/bsfs/triple_store/sparql/distance.py
index 9b58088..2c2f355 100644
--- a/bsfs/triple_store/sparql/distance.py
+++ b/bsfs/triple_store/sparql/distance.py
@@ -43,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/sparql.py b/bsfs/triple_store/sparql/sparql.py
index 68c0027..99e67d6 100644
--- a/bsfs/triple_store/sparql/sparql.py
+++ b/bsfs/triple_store/sparql/sparql.py
@@ -28,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."""
@@ -335,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):