[schema] Fix iter_all_rdefs utility function to skip rdef not reachable from the container
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 23 Mar 2017 11:59:06 +0100
changeset 2526 d3dee90b9183
parent 2519 b91f4a405baf
child 2531 02f4b849a80f
[schema] Fix iter_all_rdefs utility function to skip rdef not reachable from the container The previous implementation was yielding rdefs which were not in the compound graph, because of the blind loop on every rdef of an rtype. To fix this, we can't use structure_def whose data structure isn't adapted to this use-case, rather iterate top-down from the root (this 'child_structure' method is unfortunatly not in compound.Graph, we might add it there at some point).
cubicweb_seda/__init__.py
test/test_schema.py
--- a/cubicweb_seda/__init__.py	Tue Mar 21 14:37:35 2017 +0100
+++ b/cubicweb_seda/__init__.py	Thu Mar 23 11:59:06 2017 +0100
@@ -18,9 +18,7 @@
 Data Exchange Standard for Archival
 """
 
-from cubicweb import neg_role
-
-from cubicweb_compound import structure_def, skip_rtypes_set
+from cubicweb_compound import structure_def, skip_rtypes_set, CompositeGraph
 
 
 def seda_profile_container_def(schema):
@@ -46,11 +44,22 @@
     """Return an iterator on (rdef, role) of all relations of the compound graph starting from the
     given entity type, both internal (composite) and external (non-composite).
     """
-    for etype, parent_rdefs in structure_def(schema, container_etype).items():
-        for rtype, role in parent_rdefs:
-            for rdef in schema[rtype].rdefs.values():
-                yield rdef, neg_role(role)
+    graph = CompositeGraph(schema)
+    stack = [container_etype]
+    visited = set(stack)
+    while stack:
+        etype = stack.pop()
+        for (rtype, role), targets in graph.child_relations(etype):
+            rschema = schema.rschema(rtype)
+            for target in targets:
+                if role == 'subject':
+                    rdef = rschema.rdefs[(etype, target)]
+                else:
+                    rdef = rschema.rdefs[(target, etype)]
+                yield rdef, role
+
+                if target not in visited:
+                    visited.add(target)
+                    stack.append(target)
         for rdef, role in iter_external_rdefs(schema[etype]):
-                yield rdef, role
-    for rdef, role in iter_external_rdefs(schema[container_etype]):
-        yield rdef, role
+            yield rdef, role
--- a/test/test_schema.py	Tue Mar 21 14:37:35 2017 +0100
+++ b/test/test_schema.py	Thu Mar 23 11:59:06 2017 +0100
@@ -21,10 +21,13 @@
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.schema import ERQLExpression, RRQLExpression
 
+from cubicweb_seda import iter_all_rdefs
+
 import testutils
 
 
 class SchemaConceptConstraintsTC(CubicWebTC):
+
     def setup_database(self):
         with self.admin_access.client_cnx() as cnx:
             mt_scheme = testutils.scheme_for_type(cnx, 'seda_mime_type_to', None)
@@ -364,6 +367,20 @@
             self.assertTrue(cloned.first_level_choice.content_sequence)
 
 
+class SchemaIteratorTC(CubicWebTC):
+
+    def test_iter_all_rdefs(self):
+        values = {str(rdef) for rdef, role in iter_all_rdefs(self.schema, 'SEDABinaryDataObject')}
+        # composite relation
+        self.assertIn('relation SEDACreatingOs seda_creating_os SEDABinaryDataObject', values)
+        # external relation
+        self.assertIn('relation SEDARelationship seda_type_relationship Concept', values)
+        # outside this compound graph
+        self.assertNotIn(
+            'relation SEDADataObjectVersion seda_data_object_version_from SEDAPhysicalDataObject',
+            values)
+
+
 if __name__ == '__main__':
     import unittest
     unittest.main()