cubicweb/rtags.py
changeset 11888 0849a5eb57b8
parent 11886 c5c041ba35b8
child 11932 7b2247098f58
--- a/cubicweb/rtags.py	Wed Nov 09 16:08:23 2016 +0100
+++ b/cubicweb/rtags.py	Wed Nov 09 16:14:17 2016 +0100
@@ -49,6 +49,15 @@
     return tuple(str(k) for k in key)
 
 
+def rtags_chain(rtag):
+    """Return the rtags chain, starting from the given one, and going back through each parent rtag
+    up to the root (i.e. which as no parent).
+    """
+    while rtag is not None:
+        yield rtag
+        rtag = rtag._parent
+
+
 class RegistrableRtags(RegistrableInstance):
     __registry__ = 'uicfg'
     __select__ = yes()
@@ -68,8 +77,12 @@
     # function given as __init__ argument and kept for bw compat
     _init = _initfunc = None
 
-    def __init__(self):
+    def __init__(self, parent=None):
         self._tagdefs = {}
+        self._parent = parent
+        if parent is not None:
+            assert parent.__class__ is self.__class__, \
+                'inconsistent class for parent rtag {0}'.format(parent)
 
     def __repr__(self):
         # find a way to have more infos but keep it readable
@@ -105,7 +118,7 @@
                                      (stype, rtype, otype, tagged), value, ertype)
                         self.del_rtag(stype, rtype, otype, tagged)
                         break
-        if self._init is not None:
+        if self._parent is None and self._init is not None:
             self.apply(schema, self._init)
 
     def apply(self, schema, func):
@@ -122,6 +135,19 @@
 
     # rtag declaration api ####################################################
 
+    def derive(self, module, select):
+        """Return a derivated of this relation tag, associated to given module and selector.
+
+        This derivated will hold a set of specific rules but delegate to its "parent" relation tags
+        for unfound keys.
+
+        >>> class_afs = uicfg.autoform_section.derive(__name__, is_instance('Class'))
+        """
+        copied = self.__class__(self)
+        copied.__module__ = module
+        copied.__select__ = select
+        return copied
+
     def tag_attribute(self, key, *args, **kwargs):
         key = list(key)
         key.append('*')
@@ -163,11 +189,15 @@
         del self._tagdefs[key]
 
     def get(self, *key):
+        """Return value for the given key, by looking from the most specific key to the more
+        generic (using '*' wildcards). For each key, look into this rtag and its parent rtags.
+        """
         for key in reversed(self._get_keys(*key)):
-            try:
-                return self._tagdefs[key]
-            except KeyError:
-                continue
+            for rtag in rtags_chain(self):
+                try:
+                    return rtag._tagdefs[key]
+                except KeyError:
+                    continue
         return None
 
     def etype_get(self, etype, rtype, role, ttype='*'):
@@ -192,12 +222,18 @@
         return rtags
 
     def get(self, stype, rtype, otype, tagged):
+        """Return value for the given key, which is an union of the values found from the most
+        specific key to the more generic (using '*' wildcards). For each key, look into this rtag
+        and its parent rtags.
+        """
         rtags = self.tag_container_cls()
         for key in self._get_keys(stype, rtype, otype, tagged):
-            try:
-                rtags.update(self._tagdefs[key])
-            except KeyError:
-                continue
+            for rtag in rtags_chain(self):
+                try:
+                    rtags.update(rtag._tagdefs[key])
+                    break
+                except KeyError:
+                    continue
         return rtags