diff options
author | Matthias Baumgartner <dev@igsor.net> | 2023-03-05 19:25:17 +0100 |
---|---|---|
committer | Matthias Baumgartner <dev@igsor.net> | 2023-03-05 19:25:17 +0100 |
commit | e4845c627e97a6d125bf33d9e7a4a8d373d7fc4a (patch) | |
tree | 634198c34aae3c0306ce30ac7452abd7b53a14e8 /bsfs/graph/nodes.py | |
parent | 9b490d19dcebc0fc24cb2ab89a783f1f7d6147f7 (diff) | |
parent | 3ae93a405724ca6b5ddeb0b458fcc95685f83f09 (diff) | |
download | bsfs-e4845c627e97a6d125bf33d9e7a4a8d373d7fc4a.tar.gz bsfs-e4845c627e97a6d125bf33d9e7a4a8d373d7fc4a.tar.bz2 bsfs-e4845c627e97a6d125bf33d9e7a4a8d373d7fc4a.zip |
Merge branch 'mb/diogenes' into develop
Diffstat (limited to 'bsfs/graph/nodes.py')
-rw-r--r-- | bsfs/graph/nodes.py | 113 |
1 files changed, 57 insertions, 56 deletions
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 |