aboutsummaryrefslogtreecommitdiffstats
path: root/bsfs/schema/schema.py
diff options
context:
space:
mode:
Diffstat (limited to 'bsfs/schema/schema.py')
-rw-r--r--bsfs/schema/schema.py56
1 files changed, 41 insertions, 15 deletions
diff --git a/bsfs/schema/schema.py b/bsfs/schema/schema.py
index b6f37a7..c5d4571 100644
--- a/bsfs/schema/schema.py
+++ b/bsfs/schema/schema.py
@@ -25,11 +25,28 @@ __all__: typing.Sequence[str] = (
## code ##
class Schema():
- """
+ """Graph schema.
+
+ Use `Schema.Empty()` to create a new, empty Schema rather than construct
+ it directly.
+
+ The schema is defined by three sets: Predicates, Nodes, and Literals.
+
+ The Schema class guarantees two properties: completeness and consistency.
+ Completeness means that the schema covers all class that are referred to
+ by any other class in the schema. Consistency means that each class is
+ identified by a unique URI and all classes that use that URI consequently
+ use the same definition.
+
"""
+ # node classes.
_nodes: typing.Dict[URI, types.Node]
+
+ # literal classes.
_literals: typing.Dict[URI, types.Literal]
+
+ # predicate classes.
_predicates: typing.Dict[URI, types.Predicate]
def __init__(
@@ -47,7 +64,8 @@ class Schema():
literals = set(literals)
predicates = set(predicates)
# include parents in predicates set
- predicates |= {par for pred in predicates for par in pred.parents()}
+ # TODO: review type annotations and ignores for python >= 3.11 (parents is _Type but should be typing.Self)
+ predicates |= {par for pred in predicates for par in pred.parents()} # type: ignore [misc]
# include predicate domain in nodes set
nodes |= {pred.domain for pred in predicates}
# include predicate range in nodes and literals sets
@@ -57,8 +75,8 @@ class Schema():
# include parents in nodes and literals sets
# NOTE: Must be done after predicate domain/range was handled
# so that their parents are included as well.
- nodes |= {par for node in nodes for par in node.parents()}
- literals |= {par for lit in literals for par in lit.parents()}
+ nodes |= {par for node in nodes for par in node.parents()} # type: ignore [misc]
+ literals |= {par for lit in literals for par in lit.parents()} # type: ignore [misc]
# assign members
self._nodes = {node.uri: node for node in nodes}
self._literals = {lit.uri: lit for lit in literals}
@@ -153,9 +171,7 @@ class Schema():
return self.diff(other)
def consistent_with(self, other: 'Schema') -> bool:
- """Checks if two schemas have different definitions for the same uri.
- Tests nodes, literals, and predicates.
- """
+ """Checks if two schemas have different predicate, node, or literal definitions for the same uri."""
# check arg
if not isinstance(other, Schema):
raise TypeError(other)
@@ -181,7 +197,10 @@ class Schema():
return True
@classmethod
- def Union(cls, *args: typing.Union['Schema', typing.Iterable['Schema']]) -> 'Schema':
+ def Union( # pylint: disable=invalid-name # capitalized classmethod
+ cls,
+ *args: typing.Union['Schema', typing.Iterable['Schema']]
+ ) -> 'Schema':
"""Combine multiple Schema instances into a single one.
As argument, you can either pass multiple Schema instances, or a single
iterable over Schema instances. Any abc.Iterable will be accepted.
@@ -200,7 +219,7 @@ class Schema():
if isinstance(args[0], cls): # args is sequence of Schema instances
pass
elif len(args) == 1 and isinstance(args[0], abc.Iterable): # args is a single iterable
- args = args[0]
+ args = args[0] # type: ignore [assignment] # we checked and thus know that args[0] is an iterable
else:
raise TypeError(f'expected multiple Schema instances or a single Iterable, found {args}')
@@ -237,25 +256,31 @@ class Schema():
## getters ##
- # FIXME: which of the getters below are actually needed?
+ # FIXME: nodes, predicates, literals could be properties
# FIXME: interchangeability of URI and _Type?!
def has_node(self, node: URI) -> bool:
+ """Return True if a Node with URI *node* is part of the schema."""
return node in self._nodes
def has_literal(self, lit: URI) -> bool:
+ """Return True if a Literal with URI *lit* is part of the schema."""
return lit in self._literals
def has_predicate(self, pred: URI) -> bool:
+ """Return True if a Predicate with URI *pred* is part of the schema."""
return pred in self._predicates
- def nodes(self) -> typing.Iterator[types.Node]: # FIXME: type annotation
+ def nodes(self) -> typing.Iterable[types.Node]:
+ """Return an iterator over Node classes."""
return self._nodes.values()
- def literals(self) -> typing.Iterator[types.Literal]: # FIXME: type annotation
+ def literals(self) -> typing.Iterable[types.Literal]:
+ """Return an iterator over Literal classes."""
return self._literals.values()
- def predicates(self) -> typing.Iterator[types.Predicate]: # FIXME: type annotation
+ def predicates(self) -> typing.Iterable[types.Predicate]:
+ """Return an iterator over Predicate classes."""
return self._predicates.values()
def node(self, uri: URI) -> types.Node:
@@ -275,7 +300,8 @@ class Schema():
@classmethod
- def Empty(cls) -> 'Schema':
+ def Empty(cls) -> 'Schema': # pylint: disable=invalid-name # capitalized classmethod
+ """Return a minimal Schema."""
node = types.Node(ns.bsfs.Node, None)
literal = types.Literal(ns.bsfs.Literal, None)
predicate = types.Predicate(
@@ -289,7 +315,7 @@ class Schema():
@classmethod
- def from_string(cls, schema: str) -> 'Schema':
+ def from_string(cls, schema: str) -> 'Schema': # pylint: disable=invalid-name # capitalized classmethod
"""Load and return a Schema from a string."""
# parse string into rdf graph
graph = rdflib.Graph()