aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bsfs/schema/schema.py35
-rw-r--r--test/schema/test_schema.py129
2 files changed, 164 insertions, 0 deletions
diff --git a/bsfs/schema/schema.py b/bsfs/schema/schema.py
index 0e053c0..b6f37a7 100644
--- a/bsfs/schema/schema.py
+++ b/bsfs/schema/schema.py
@@ -103,6 +103,41 @@ class Schema():
SchemaDiff = namedtuple('SchemaDiff', ['nodes', 'literals', 'predicates'])
+ def _issubset(self, other: 'Schema') -> bool:
+ # inconsistent schema can't be ordered.
+ if not self.consistent_with(other):
+ return False
+ # since schemas are consistent, it's sufficient to compare their URIs.
+ # self's sets are fully contained in other's sets
+ # pylint: disable=protected-access
+ return set(self._predicates) <= set(other._predicates) \
+ and set(self._nodes) <= set(other._nodes) \
+ and set(self._literals) <= set(other._literals)
+
+ def __lt__(self, other: typing.Any) -> bool:
+ """Return True if *other* is a true subset of *self*."""
+ if not isinstance(other, Schema): # other is not a Schema
+ return NotImplemented
+ return self != other and self._issubset(other)
+
+ def __le__(self, other: typing.Any) -> bool:
+ """Return True if *other* is a subset of *self*."""
+ if not isinstance(other, Schema): # other is not a Schema
+ return NotImplemented
+ return self == other or self._issubset(other)
+
+ def __gt__(self, other: typing.Any) -> bool:
+ """Return True if *other* is a true superset of *self*."""
+ if not isinstance(other, Schema): # other is not a Schema
+ return NotImplemented
+ return self != other and other._issubset(self)
+
+ def __ge__(self, other: typing.Any) -> bool:
+ """Return True if *other* is a superset of *self*."""
+ if not isinstance(other, Schema): # other is not a Schema
+ return NotImplemented
+ return self == other or other._issubset(self)
+
def diff(self, other: 'Schema') -> SchemaDiff:
"""Return node, literals, and predicates that are in *self* but not in *other*."""
return self.SchemaDiff(
diff --git a/test/schema/test_schema.py b/test/schema/test_schema.py
index 2dc26e8..888cdca 100644
--- a/test/schema/test_schema.py
+++ b/test/schema/test_schema.py
@@ -206,6 +206,135 @@ class TestSchema(unittest.TestCase):
self.assertNotEqual(hash(schema),
hash(Schema(self.predicates + [self.p_root.get_child(ns.bse.filesize, self.n_ent, self.l_integer)], self.nodes, self.literals)))
+ def test_order(self):
+ # setup
+ class Foo(): pass
+ p_foo = self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, True)
+ p_sub = p_foo.get_child(ns.bse.sub, self.n_ent, self.l_string, True)
+ p_bar = self.p_root.get_child(ns.bse.bar, self.n_ent, self.l_string, True)
+
+ # can only compare schema to other schema
+ # <
+ self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), 'hello world')
+ self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), 1234)
+ self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), p_foo)
+ self.assertRaises(TypeError, operator.lt, Schema({p_foo, p_bar}), Foo())
+ # <=
+ self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), 'hello world')
+ self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), 1234)
+ self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), p_foo)
+ self.assertRaises(TypeError, operator.le, Schema({p_foo, p_bar}), Foo())
+ # >
+ self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), 'hello world')
+ self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), 1234)
+ self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), p_foo)
+ self.assertRaises(TypeError, operator.gt, Schema({p_foo, p_bar}), Foo())
+ # >=
+ self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), 'hello world')
+ self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), 1234)
+ self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), p_foo)
+ self.assertRaises(TypeError, operator.ge, Schema({p_foo, p_bar}), Foo())
+
+ # a schema is a subset of itself
+ self.assertTrue(operator.le(Schema({self.p_tag}), Schema({self.p_tag})))
+ # a schema is a superset of itself
+ self.assertTrue(operator.ge(Schema({self.p_tag}), Schema({self.p_tag})))
+ # a schema is not a true subset of itself
+ self.assertFalse(operator.lt(Schema({self.p_tag}), Schema({self.p_tag})))
+ # a schema is not a true superset of itself
+ self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_tag})))
+
+ # subset considers predicates
+ self.assertTrue(operator.lt(Schema({p_foo}), Schema({p_foo, p_bar})))
+ self.assertTrue(operator.lt(Schema({p_foo}), Schema({p_sub})))
+ self.assertFalse(operator.lt(Schema({p_foo}), Schema({p_bar})))
+ # subset considers nodes
+ self.assertTrue(operator.lt(Schema({self.p_tag}), Schema({self.p_tag}, {self.n_unused})))
+ self.assertFalse(operator.lt(Schema({self.p_tag}, {self.n_unused}), Schema({self.p_tag})))
+ # subset considers literals
+ self.assertTrue(operator.lt(Schema({self.p_tag}), Schema({self.p_tag}, {}, {self.l_unused})))
+ self.assertFalse(operator.lt(Schema({self.p_tag}, {}, {self.l_unused}), Schema({self.p_tag})))
+ # subset considers differences in predicates and nodes
+ self.assertTrue(operator.lt(Schema({self.p_tag}), Schema({self.p_group})))
+ self.assertTrue(operator.le(Schema({self.p_tag}), Schema({self.p_group})))
+ # subset considers differences in predicates and literals
+ self.assertTrue(operator.lt(Schema.Empty(), Schema({self.p_comment})))
+ # subset considers differences in predicates, nodes, and literals
+ self.assertTrue(operator.lt(Schema({}), Schema.Empty()))
+ self.assertTrue(operator.lt(Schema({self.p_tag}), Schema.from_string(self.schema_str)))
+ self.assertTrue(operator.le(Schema({self.p_tag}), Schema.from_string(self.schema_str)))
+ self.assertFalse(operator.lt(Schema({self.p_comment}), Schema({self.p_tag})))
+ self.assertFalse(operator.le(Schema({self.p_comment}), Schema({self.p_tag})))
+
+ # superset considers predicates
+ self.assertTrue(operator.gt(Schema({p_foo, p_bar}), Schema({p_foo})))
+ self.assertTrue(operator.gt(Schema({p_sub}), Schema({p_foo})))
+ self.assertFalse(operator.gt(Schema({p_foo}), Schema({p_bar})))
+ # superset considers nodes
+ self.assertTrue(operator.gt(Schema({self.p_tag}, {self.n_unused}), Schema({self.p_tag})))
+ self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_tag}, {self.n_unused})))
+ # superset considers literals
+ self.assertTrue(operator.gt(Schema({self.p_tag}, {}, {self.l_unused}), Schema({self.p_tag})))
+ self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_tag}, {}, {self.l_unused})))
+ # superset considers differences in predicates and nodes
+ self.assertTrue(operator.gt(Schema({self.p_group}), Schema({self.p_tag})))
+ self.assertTrue(operator.ge(Schema({self.p_group}), Schema({self.p_tag})))
+ # superset considers differences in predicates and literals
+ self.assertTrue(operator.gt(Schema({self.p_comment}), Schema.Empty()))
+ # superset considers differences in predicates, nodes, and literals
+ self.assertTrue(operator.gt(Schema.Empty(), Schema({})))
+ self.assertTrue(operator.gt(Schema.from_string(self.schema_str), Schema({self.p_tag})))
+ self.assertTrue(operator.ge(Schema.from_string(self.schema_str), Schema({self.p_tag})))
+ self.assertFalse(operator.gt(Schema({self.p_tag}), Schema({self.p_comment})))
+ self.assertFalse(operator.ge(Schema({self.p_tag}), Schema({self.p_comment})))
+
+ # inconsistent schema cannot be a subset
+ self.assertFalse(operator.le(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal
+ self.assertFalse(operator.le(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node
+ self.assertFalse(operator.le(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique
+ self.assertFalse(operator.le(Schema({}, {self.n_img}), Schema({}, {
+ types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))})))
+ self.assertFalse(operator.le(Schema({}, {}, {self.l_integer}), Schema({}, {}, {
+ types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))})))
+ # inconsistent schema cannot be a true subset
+ self.assertFalse(operator.lt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal
+ self.assertFalse(operator.lt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node
+ self.assertFalse(operator.lt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique
+ self.assertFalse(operator.lt(Schema({}, {self.n_img}), Schema({}, {
+ types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))})))
+ self.assertFalse(operator.lt(Schema({}, {}, {self.l_integer}), Schema({}, {}, {
+ types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))})))
+ # inconsistent schema cannot be a superset
+ self.assertFalse(operator.ge(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal
+ self.assertFalse(operator.ge(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node
+ self.assertFalse(operator.ge(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique
+ self.assertFalse(operator.ge(Schema({}, {self.n_img}), Schema({}, {
+ types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))})))
+ self.assertFalse(operator.ge(Schema({}, {}, {self.l_integer}), Schema({}, {}, {
+ types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))})))
+ # inconsistent schema cannot be a true superset
+ self.assertFalse(operator.gt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_integer, True)}))) # inconsistent w.r.t. literal
+ self.assertFalse(operator.gt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_img, self.l_string, True)}))) # inconsistent w.r.t. node
+ self.assertFalse(operator.gt(Schema({p_foo}), Schema({
+ self.p_root.get_child(ns.bse.foo, self.n_ent, self.l_string, False)}))) # inconsistent w.r.t. unique
+ self.assertFalse(operator.gt(Schema({}, {self.n_img}), Schema({}, {
+ types.Node(ns.bsfs.Image, types.Node(ns.bsfs.Node, None))})))
+ self.assertFalse(operator.gt(Schema({}, {}, {self.l_integer}), Schema({}, {}, {
+ types.Literal(ns.xsd.integer, types.Literal(ns.xsd.number, types.Literal(ns.bsfs.Literal, None)))})))
+
+
+
def test_diff(self):
# difference can be empty
diff = Schema({self.p_tag}).diff(Schema({self.p_group}))