diff configmix/config.py @ 656:2b1c7a68f913

Enable indexed access to lists in the configuration using an access path string representation like "~NNN~"
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 30 May 2022 09:31:29 +0200
parents b74f20e19c01
children 0eff8441c4b9
line wrap: on
line diff
--- a/configmix/config.py	Sun May 29 15:32:54 2022 +0200
+++ b/configmix/config.py	Mon May 30 09:31:29 2022 +0200
@@ -243,6 +243,7 @@
 _STARTTOK_REF = _STARTTOK + REF_NAMESPACE + _NS_SEPARATOR
 _ENDTOK_REF = _ENDTOK
 _DOT = u(b'.')
+_TILDE = u(b'~')
 _QUOTE = u(b'%')
 _QUOTE_x = u(b'x')
 _QUOTE_u = u(b'u')
@@ -260,11 +261,12 @@
     0x7d: u(b'%x7d'),
     0x5b: u(b'%x5b'),
     0x5d: u(b'%x5d'),
+    0x7e: u(b'%x7e'),    # tilde `~`
 }
 _QUOTE_SAFE = u(b'abcdefghijklmnopqrstuvwxyz'
                 b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
                 b'0123456789'
-                b'-_@!$&/\\()=?*+~;,<>^')
+                b'-_@!$&/\\()=?*+;,<>^')
 """Mostly used configuration key characters that do not need any quoting
 
 """
@@ -281,9 +283,19 @@
     See also the :ref:`quoting` section.
 
     """
-    # Quick check whether all of the chars are in _QUOTE_SAFE
-    if not s.lstrip(_QUOTE_SAFE):
-        return s
+    try:
+        # Quick check whether all of the chars are in _QUOTE_SAFE
+        if not s.lstrip(_QUOTE_SAFE):
+            return s
+    except AttributeError:
+        #
+        # Check whether s is an index (int) and return the special tag if
+        # it is so
+        #
+        if isinstance(s, int):
+            return "~%d~" % (s, )
+        else:
+            raise
 
     # Slow path
     re_encode = False
@@ -312,6 +324,15 @@
     This is the inverse of :func:`.quote`.
 
     """
+    s_len = len(s)
+    if s_len > 2 and s[0] == _TILDE and s[-1] == _TILDE:
+        try:
+            v = int(s[1:-1], 10)
+            if v // 10 > 3275:  # be compatible to the fast C implementation
+                raise OverflowError("index too large")
+            return v
+        except (ValueError, OverflowError):
+            pass
     if _QUOTE not in s:
         return s
     parts = s.split(_QUOTE)