Mercurial > hgrepos > Python > libs > ConfigMix
changeset 705:0485a033c95d
FIX: Parsing a filter chain for the new filter-only expansions: parse them backwards and use "," as filter-chain separator here.
This allows using filter chains in filter-only expansions also.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Tue, 15 Aug 2023 09:34:49 +0200 |
| parents | 457ef358c1a0 |
| children | 59a3fb7fcac3 |
| files | configmix/_speedups.c configmix/config.py tests/test.py |
| diffstat | 3 files changed, 70 insertions(+), 21 deletions(-) [+] |
line wrap: on
line diff
--- a/configmix/_speedups.c Mon Aug 14 13:00:19 2023 +0200 +++ b/configmix/_speedups.c Tue Aug 15 09:34:49 2023 +0200 @@ -25,6 +25,7 @@ PyObject *QUOTE; PyObject *NS_SEPARATOR; PyObject *FILTER_SEPARATOR; + PyObject *FILTER_SEPARATOR_REV; PyObject *EMPTY_FILTER; PyObject *NONE_FILTER; PyObject *EMPTY_STR; @@ -606,7 +607,7 @@ static PyObject * -_fast_split_filters(PyObject *varname, PyObject *self, struct speedups_state *sstate) +_fast_split_filters(PyObject *varname, PyObject *self, Py_ssize_t direction, struct speedups_state *sstate) { Py_ssize_t varname_len; Py_ssize_t sep; @@ -623,7 +624,7 @@ sep = -1; } else { - sep = PyUnicode_FindChar(varname, '|', 0, varname_len, 1); + sep = PyUnicode_FindChar(varname, '|', 0, varname_len, (int)direction); if (sep == -2) { return NULL; } @@ -682,7 +683,12 @@ } } - tmp = PyUnicode_Split(filters, sstate->FILTER_SEPARATOR, -1); + if (direction == 1) { + tmp = PyUnicode_Split(filters, sstate->FILTER_SEPARATOR, -1); + } + else { + tmp = PyUnicode_Split(filters, sstate->FILTER_SEPARATOR_REV, -1); + } if (tmp == NULL) { goto error; } @@ -706,9 +712,28 @@ static PyObject * -fast_split_filters(PyObject *self, PyObject *varname) +fast_split_filters(PyObject *self, PyObject *args) { - return _fast_split_filters(varname, self, NULL); + PyObject *varname; + PyObject *direction_obj; + Py_ssize_t direction; + + if (!PyArg_UnpackTuple(args, "fast_split_filters", 2, 2, &varname, &direction_obj)) { + return NULL; + } + direction = PyNumber_AsSsize_t(direction_obj, PyExc_OverflowError); + if (direction == -1) { + if (PyErr_Occurred()) { + return NULL; + } + } + if ((direction == 1) || (direction == -1)) { + return _fast_split_filters(varname, self, direction, NULL); + } + else { + PyErr_SetString(PyExc_ValueError, "`direction' must be -1 or +1"); + return NULL; + } } @@ -910,7 +935,7 @@ goto error; } - tmp = _fast_split_filters(varname, NULL, sstate); + tmp = _fast_split_filters(varname, NULL, 1, sstate); if (tmp == NULL) { goto error; } @@ -1130,7 +1155,7 @@ if (tmp == NULL) { return NULL; } - filters = _fast_split_filters(tmp, NULL, sstate); + filters = _fast_split_filters(tmp, NULL, -1, sstate); if (filters == NULL) { py_clear_ref(&tmp); return NULL; @@ -1215,7 +1240,7 @@ goto error; } - tmp = _fast_split_filters(varname, NULL, sstate); + tmp = _fast_split_filters(varname, NULL, 1, sstate); if (tmp == NULL) { goto error; } @@ -1709,7 +1734,7 @@ {"fast_unquote", fast_unquote, METH_O, PyDoc_STR("C-implementation of configmix.unquote")}, {"fast_quote", fast_quote, METH_O, PyDoc_STR("C-implementation of configmix.quote")}, {"fast_pathstr2path", fast_pathstr2path, METH_O, PyDoc_STR("C-implementation of configmix.pathstr2path")}, - {"_fast_split_filters", fast_split_filters, METH_O, PyDoc_STR("C-implementation of configmix.config._split_filters")}, + {"_fast_split_filters", fast_split_filters, METH_VARARGS, PyDoc_STR("C-implementation of configmix.config._split_filters")}, {"_fast_split_ns", fast_split_ns, METH_O, PyDoc_STR("C-implementation of configmix.config._split_ns")}, {"_fast_interpolate_variables", fast_interpolate_variables, METH_VARARGS, PyDoc_STR("C-implementation of configmix.config.Configuration.interpolate_variables")}, {"_fast_getvarl", (PyCFunction)fast_getvarl, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("C-Implementation of configmix.config.Configuration.getvarl")}, @@ -1767,6 +1792,12 @@ } PyUnicode_InternInPlace(&(sstate->FILTER_SEPARATOR)); + sstate->FILTER_SEPARATOR_REV = PyUnicode_FromStringAndSize(",", 1); + if (sstate->FILTER_SEPARATOR_REV == NULL) { + return -1; + } + PyUnicode_InternInPlace(&(sstate->FILTER_SEPARATOR_REV)); + sstate->EMPTY_FILTER = PyUnicode_FromStringAndSize("Empty", 5); if (sstate->EMPTY_FILTER == NULL) { return -1; @@ -1848,6 +1879,7 @@ Py_VISIT(sstate->QUOTE); Py_VISIT(sstate->NS_SEPARATOR); Py_VISIT(sstate->FILTER_SEPARATOR); + Py_VISIT(sstate->FILTER_SEPARATOR_REV); Py_VISIT(sstate->EMPTY_FILTER); Py_VISIT(sstate->NONE_FILTER); Py_VISIT(sstate->EMPTY_STR); @@ -1875,6 +1907,7 @@ Py_CLEAR(sstate->QUOTE); Py_CLEAR(sstate->NS_SEPARATOR); Py_CLEAR(sstate->FILTER_SEPARATOR); + Py_CLEAR(sstate->FILTER_SEPARATOR_REV); Py_CLEAR(sstate->EMPTY_FILTER); Py_CLEAR(sstate->NONE_FILTER); Py_CLEAR(sstate->EMPTY_STR);
--- a/configmix/config.py Mon Aug 14 13:00:19 2023 +0200 +++ b/configmix/config.py Tue Aug 15 09:34:49 2023 +0200 @@ -240,6 +240,7 @@ _HIER_SEPARATOR = u(b'.') _NS_SEPARATOR = u(b':') _FILTER_SEPARATOR = u(b'|') +_FILTER_SEPARATOR_REV = u(b',') _STARTTOK_REF = _STARTTOK + REF_NAMESPACE + _NS_SEPARATOR _ENDTOK_REF = _ENDTOK _ENDTOK_FILTER = _FILTER_SEPARATOR + _ENDTOK @@ -430,20 +431,29 @@ _split_ns = _py_split_ns -def _py_split_filters(varname): +def _py_split_filters(varname, direction): """Split off the filter part from the `varname` string - :type varname: str + :param str varname: The string where to search for filters + :param int direction: +1 means to do a forward search, -1 a backward search :return: The tuple of the variable name without the filters and a list of filters :rtype: tuple(str, list) """ - name, sep, filters = varname.partition(_FILTER_SEPARATOR) + if direction == 1: + name, sep, filters = varname.partition(_FILTER_SEPARATOR) + elif direction == -1: + name, sep, filters = varname.rpartition(_FILTER_SEPARATOR) + else: + raise ValueError("`direction' must be -1 or +1") if sep: filters = filters.strip() if filters: - return (name, filters.split(_FILTER_SEPARATOR)) + if direction == 1: + return (name, filters.split(_FILTER_SEPARATOR)) + else: + return (name, filters.split(_FILTER_SEPARATOR_REV)) else: return (name, []) else: @@ -1016,7 +1026,7 @@ 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]) + new_s, filters = _split_filters(s[3:-3], -1) try: varvalue = self.py_interpolate_variables(new_s) except KeyError: @@ -1039,7 +1049,7 @@ rest = start break varname, filters = _split_filters( - s[start+2:end]) # noqa: E226 + s[start+2:end], 1) # noqa: E226 try: varvalue, cacheable = self._py_getvar_s_with_cache_info(varname) except KeyError:
--- a/tests/test.py Mon Aug 14 13:00:19 2023 +0200 +++ b/tests/test.py Tue Aug 15 09:34:49 2023 +0200 @@ -2211,22 +2211,23 @@ self.assertEqual((u":", u"%x3a"), self.split_ns(u"%x3a:%x3a")) def test_split_filters_empty(self): - self.assertEqual((u"", []), self.split_filters(u"")) + self.assertEqual((u"", []), self.split_filters(u"", 1)) def test_split_filters_varname_only_no_stripping(self): - self.assertEqual((u" varname ", []), self.split_filters(u" varname ")) + self.assertEqual((u" varname ", []), + self.split_filters(u" varname ", 1)) def test_split_filters_single_no_stripping(self): self.assertEqual((u" the-varname ", []), - self.split_filters(u" the-varname | ")) + self.split_filters(u" the-varname | ", 1)) def test_split_filters_one(self): self.assertEqual((u"the-varname", [u"None"]), - self.split_filters(u"the-varname|None")) + self.split_filters(u"the-varname|None", 1)) def test_split_filters_many(self): self.assertEqual((u"the-varname", [u"Empty", u"None"]), - self.split_filters(u"the-varname|Empty|None")) + self.split_filters(u"the-varname|Empty|None", 1)) def test_None_filter_single(self): cfg = configmix.load() @@ -2421,9 +2422,14 @@ y = getattr(cfg, self.interpolate_meth)(u"{{|{{not-existing}}|Empty|}}") self.assertEqual(u(""), y) + def test_global_nested_filter_empty(self): + cfg = configmix.load() + y = getattr(cfg, self.interpolate_meth)(u"{{|pre-{{not-existing|Empty}}-post|upper|}}") + self.assertEqual(u("PRE--POST"), y) + def test_global_filter_upper(self): cfg1 = { - u("key-1"): u("value for key-1") + 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|}}")
