# HG changeset patch # User Franz Glasner # Date 1691998287 -7200 # Node ID 193a616e0b3cd02dd2ecf7efb89b3a2cf08c507f # Parent 58dc57bed012791f07def8889c8701ee84f8c907 Begin implementation of filter-only expansions (recursive with respect to expansion) diff -r 58dc57bed012 -r 193a616e0b3c configmix/_speedups.c --- 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); } diff -r 58dc57bed012 -r 193a616e0b3c configmix/config.py --- 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 diff -r 58dc57bed012 -r 193a616e0b3c tests/test.py --- 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):