diff configmix/config.py @ 320:98490375d90c

Allow variable name quoting to be used in .getvar() and .getvar_s() and references
author Franz Glasner <fzglas.hg@dom66.de>
date Thu, 06 May 2021 09:45:51 +0200
parents 043a6412be3c
children 7a0f3c256cf4
line wrap: on
line diff
--- a/configmix/config.py	Wed May 05 23:12:01 2021 +0200
+++ b/configmix/config.py	Thu May 06 09:45:51 2021 +0200
@@ -28,7 +28,7 @@
     from urlparse import urlsplit
 
 from .variables import lookup_varns, lookup_filter
-from .compat import u
+from .compat import u, uchr
 from .constants import REF_NAMESPACE
 
 
@@ -70,6 +70,7 @@
     _STARTTOK_REF = _STARTTOK + REF_NAMESPACE + _NS_SEPARATOR
     _ENDTOK_REF = _ENDTOK
     _DOT = u(b'.')
+    _QUOTE = u(b'%')
 
     def getvarl(self, *names, default=_MARKER, namespace=None):
         """Get a variable where the hierarchy is given in `names` as sequence
@@ -104,7 +105,7 @@
         """
         varns, varname = self._split_ns(varname)
         if not varns:
-            varnameparts = varname.split(self._HIER_SEPARATOR)
+            varnameparts = [self.unquote(vp) for vp in varname.split(self._HIER_SEPARATOR)]
         else:
             varnameparts = (varname,)
         return self.getvarl(*varnameparts, namespace=varns)
@@ -365,3 +366,46 @@
             else:
                 value = filterfn(self, value)
         return value
+
+    @classmethod
+    def quote(klass, s):
+        """Quote a key to protect all dangerous chars: ``%``, ``.``, ``:``
+        and ``|``
+
+        """
+        qc = klass._QUOTE
+        s = s.replace(qc, qc + "x25")
+        s = s.replace(klass._DOT, qc + "x2e")
+        s = s.replace(klass._NS_SEPARATOR, qc + "x3a")
+        return s.replace(klass._FILTER_SEPARATOR, qc + "x7c")
+
+    @classmethod
+    def unquote(klass, s):
+        """Unquote the content of `s`: handle all patterns ``%xXX``,
+        ``%uXXXX`` or `%UXXXXXXXX``
+
+        """
+        if klass._QUOTE not in s:
+            return s
+        res = []
+        parts = s.split(klass._QUOTE)
+        res.append(parts[0])
+        for p in parts[1:]:
+            if p.startswith(u(b'x')):
+                if len(p) < 3:
+                    raise ValueError("quote syntax: length too small")
+                res.append(uchr(int(p[1:3], 16)))
+                res.append(p[3:])
+            elif p.startswith(u(b'u')):
+                if len(p) < 5:
+                    raise ValueError("quote syntax: length too small")
+                res.append(uchr(int(p[1:5], 16)))
+                res.append(p[5:])
+            elif p.startswith(u(b'U')):
+                if len(p) < 9:
+                    raise ValueError("quote syntax: length too small")
+                res.append(uchr(int(p[1:9], 16)))
+                res.append(p[9:])
+            else:
+                raise ValueError("unknown quote syntax string: {}".format(s))
+        return ''.join(res)