changeset 703:193a616e0b3c

Begin implementation of filter-only expansions (recursive with respect to expansion)
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 14 Aug 2023 09:31:27 +0200
parents 58dc57bed012
children 457ef358c1a0
files configmix/_speedups.c configmix/config.py tests/test.py
diffstat 3 files changed, 89 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/configmix/_speedups.c	Sun Aug 13 16:14:39 2023 +0200
+++ b/configmix/_speedups.c	Mon Aug 14 09:31:27 2023 +0200
@@ -33,6 +33,7 @@
     PyObject *MARKER;
     PyObject *STARTTOK;
     PyObject *ENDTOK;
+    PyObject *ENDTOK_FILTER;
     PyObject *REF_NAMESPACE;
     PyObject *DEL_VALUE;
 };
@@ -913,8 +914,8 @@
             goto error;
         }
         if (PyTuple_Size(tmp) != 2) {
+            py_clear_ref(&tmp);
             PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected");
-            py_clear_ref(&tmp);
             goto error;
         }
         py_clear_ref(&varname);
@@ -1100,6 +1101,39 @@
         }
     }
 
+    if ((s_len >= 6)
+            && (start == 0)
+            && (PyUnicode_ReadChar(s, 2) == 0x7c  /* | */)) {
+        end = PyUnicode_Find(s, sstate->ENDTOK_FILTER, start+3, s_len, 1);
+        if (end == -2) {
+            return NULL;
+        }
+        if (end != (s_len - 3)) {
+            PyErr_SetString(PyExc_ValueError, "XXX") ; /*`{{|' global filter interpolation must end with `|}}'"); */
+            return NULL;
+        }
+        tmp = PyUnicode_Substring(s, 3, s_len-3);
+        if (tmp == NULL) {
+            return NULL;
+        }
+        filters = _fast_split_filters(tmp, NULL, sstate);
+        if (filters == NULL) {
+            py_clear_ref(&tmp);
+            return NULL;
+        }
+        if (PyTuple_Size(filters) != 2) {
+            py_clear_ref(&tmp);
+            PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected");
+            goto error;
+        }
+        py_transfer_owned(&tmp, &filters);
+
+        /* XXX TBD */
+        py_clear_ref(&tmp);
+        PyErr_SetString(PyExc_KeyError, "ERRRRRRR in C");
+        return NULL;
+    }
+
     result = PyList_New(0);
     if (result == NULL) {
         return NULL;
@@ -1734,6 +1768,12 @@
     }
     PyUnicode_InternInPlace(&(sstate->ENDTOK));
 
+    sstate->ENDTOK_FILTER = PyUnicode_FromStringAndSize("|}}", 3);
+    if (sstate->ENDTOK_FILTER == NULL) {
+        return -1;
+    }
+    PyUnicode_InternInPlace(&(sstate->ENDTOK_FILTER));
+
     sstate->REF_NAMESPACE = PyUnicode_FromStringAndSize("ref", 3);
     if (sstate->REF_NAMESPACE == NULL) {
         return -1;
@@ -1769,6 +1809,7 @@
         Py_VISIT(sstate->MARKER);
         Py_VISIT(sstate->STARTTOK);
         Py_VISIT(sstate->ENDTOK);
+        Py_VISIT(sstate->ENDTOK_FILTER);
         Py_VISIT(sstate->REF_NAMESPACE);
         Py_VISIT(sstate->DEL_VALUE);
     }
@@ -1795,6 +1836,7 @@
         Py_CLEAR(sstate->MARKER);
         Py_CLEAR(sstate->STARTTOK);
         Py_CLEAR(sstate->ENDTOK);
+        Py_CLEAR(sstate->ENDTOK_FILTER);
         Py_CLEAR(sstate->REF_NAMESPACE);
         Py_CLEAR(sstate->DEL_VALUE);
     }
--- a/configmix/config.py	Sun Aug 13 16:14:39 2023 +0200
+++ b/configmix/config.py	Mon Aug 14 09:31:27 2023 +0200
@@ -242,6 +242,7 @@
 _FILTER_SEPARATOR = u(b'|')
 _STARTTOK_REF = _STARTTOK + REF_NAMESPACE + _NS_SEPARATOR
 _ENDTOK_REF = _ENDTOK
+_ENDTOK_FILTER = _FILTER_SEPARATOR + _ENDTOK
 _DOT = u(b'.')
 _TILDE = u(b'~')
 _QUOTE = u(b'%')
@@ -1009,6 +1010,24 @@
                                    "%r (cached)" % (s, ))
                 else:
                     return res
+                
+        if ((len_s >= 6)
+                and (s[2] == _FILTER_SEPARATOR)
+                and (start == 0)):
+            if s.find(_ENDTOK_FILTER, 3) != (len_s - 3):
+                raise ValueError("`{{|' global filter interpolation must end with `|}}'")
+            new_s, filters = _split_filters(s[3:-3])
+            try:
+                varvalue = self.py_interpolate_variables(new_s)
+            except KeyError:
+                if NONE_FILTER in filters:
+                    varvalue = None
+                elif EMPTY_FILTER in filters:
+                    varvalue = _EMPTY_STR
+                else:
+                    raise
+            varvalue = self._apply_filters(filters, varvalue)
+            return varvalue
         res = []
         res_append = res.append
         rest = 0
--- a/tests/test.py	Sun Aug 13 16:14:39 2023 +0200
+++ b/tests/test.py	Mon Aug 14 09:31:27 2023 +0200
@@ -2402,6 +2402,33 @@
             cfg.getvar,
             u("intl.cache"))
 
+    def test_global_filter_wrong_syntax(self):
+        cfg = configmix.load()
+        try:
+            y = getattr(cfg, self.interpolate_meth)(u"{{|{{not-existing}}|None}}")
+        except ValueError:
+            pass
+        else:
+            self.fail("`ValueError' should have been raised")
+
+    def test_global_filter_None(self):
+        cfg = configmix.load()
+        y = getattr(cfg, self.interpolate_meth)(u"{{|{{not-existing}}|None|}}")
+        self.assertTrue(y is None)
+
+    def test_global_filter_empty(self):
+        cfg = configmix.load()
+        y = getattr(cfg, self.interpolate_meth)(u"{{|{{not-existing}}|Empty|}}")
+        self.assertEqual(u(""), y)
+
+    def test_global_filter_upper(self):
+        cfg1 = {
+            u("key-1"): u("value for key-1")
+        }
+        cfg = cfg = configmix.config.Configuration(cfg1)
+        y = getattr(cfg, self.interpolate_meth)(u"{{|pre_{{key-1}}_post|upper|}}")
+        self.assertEqual(u("PRE_VALUE FOR KEY-1_POST"), y)
+
 
 class T09Parser(_TParserMixin, unittest.TestCase):