changeset 679:aa39c1856de4

Begin "ref:" support for jails. Currently jailed configurations do not work if the jail's root is located at a substitution.
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 09 Jun 2023 09:24:41 +0200
parents f39b96e2bb2a
children e71f8bd50342
files configmix/config.py
diffstat 1 files changed, 47 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/configmix/config.py	Thu Jun 08 22:14:30 2023 +0200
+++ b/configmix/config.py	Fri Jun 09 09:24:41 2023 +0200
@@ -939,6 +939,31 @@
             raise ValueError("relative refs not supported")
         return self.getvar(pu.fragment)
 
+    def try_get_reference_uri(self, v):
+        """Check whether `v` is a configuration reference and -- if true --
+        return the configuration path where the reference points to.
+
+        If `v` is not a text type or not a reference return `None`.
+
+        Does not check whether the referenced configuration object exists.
+
+        :rtype: None or str
+
+        """
+        if not isinstance(v, _TEXTTYPE):
+            return None
+        if v.startswith(_STARTTOK_REF) and v.endswith(_ENDTOK_REF):
+            uri = v[len(_STARTTOK_REF):-len(_ENDTOK_REF)]
+            pu = urlsplit(uri)
+            if pu.scheme or pu.netloc or pu.path or pu.query:
+                raise ValueError("only fragment-only URIs are supported")
+            if not pu.fragment:
+                return _EMPTY_STR
+            if pu.fragment.startswith(_DOT):
+                raise ValueError("relative refs not supported")
+            return pu.fragment
+        return None
+
     def substitute_variables_in_obj(self, obj):
         """Recursively expand variables in the object tree `obj`."""
         ty = type(obj)
@@ -1062,7 +1087,8 @@
                 value = filterfn(self, value)
         return value
 
-    def jailed(self, rootpath=None, root=None, bind_root=True):
+    def jailed(self, rootpath=None, root=None,
+               bind_root=True, resolve_ref=True):
         """Return a "jailed" configuration of the current configuration.
 
         :param rootpath: a sequence of strings (or objects) that shall
@@ -1075,6 +1101,10 @@
                                creation of a jailed config you can set
                                `bind_root` to `False`; otherwise use
                                the default
+        :param bool resolve_ref: If `True` then the jail's root is checked
+                                 whether it is a reference. If it is a
+                                 reference then the jail is effectively rooted
+                                 where the reference points to.
         :return: a jailed (aka restricted) configuration
         :rtype: _JailedConfiguration
 
@@ -1094,6 +1124,13 @@
                 raise ValueError(
                     "jailed configurations do not support namespaces")
             rootpath = pathstr2path(root)
+        if resolve_ref:
+            while True:
+                target = self.getvarl(*rootpath)
+                target_uri = self.try_get_reference_uri(target)
+                if target_uri is None:
+                    break
+                rootpath = pathstr2path(target_uri)
         jc = _JailedConfiguration(*rootpath)
         if bind_root:
             jc.rebind(self)
@@ -1323,7 +1360,8 @@
             """Map- and list-style evaluation in boolean context"""
             return bool(self._base.getvarl_s(*self._path))
 
-    def jailed(self, rootpath=None, root=None, bind_root=True):
+    def jailed(self, rootpath=None, root=None,
+               bind_root=True, resolve_ref=True):
         """Return a "jailed" configuration that effectively is a
         subjail of the current jail
 
@@ -1347,6 +1385,13 @@
             new_rootpath = self._path + tuple(rootpath)
         else:
             new_rootpath = tuple(rootpath)
+        if resolve_ref:
+            while True:
+                target = self._base.getvarl(*new_rootpath)
+                target_uri = self._base.try_get_reference_uri(target)
+                if target_uri is None:
+                    break
+                new_rootpath = pathstr2path(target_uri)
         sjc = _JailedConfiguration(*new_rootpath)
         if bind_root:
             sjc.rebind(self._base)