Mercurial > hgrepos > Python > libs > ConfigMix
changeset 552:39e5d07d8dbc
Provide a C implementation of configmix.config._split_filters.
This is needed as precondition for implementation interpolate_variables in
C also.
Currently the speedup is not measurable but it does not hurt also.
Also provide some unit-tests for _split_filters().
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sun, 02 Jan 2022 20:40:09 +0100 |
| parents | 4c968c5cfce6 |
| children | 9d2bd411f5c5 |
| files | configmix/_speedups.c configmix/config.py tests/test.py |
| diffstat | 3 files changed, 159 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/configmix/_speedups.c Sun Jan 02 16:05:35 2022 +0100 +++ b/configmix/_speedups.c Sun Jan 02 20:40:09 2022 +0100 @@ -22,6 +22,7 @@ PyObject *DOT; PyObject *QUOTE; PyObject *NS_SEPARATOR; + PyObject *FILTER_SEPARATOR; PyObject *EMPTY_STR; PyObject *QUOTE_MAP; }; @@ -251,7 +252,7 @@ static PyObject * -_fast_unquote(PyObject *self, PyObject *s, Py_ssize_t s_len, struct speedups_state *sstate) +_fast_unquote(PyObject *s, Py_ssize_t s_len, PyObject *self, struct speedups_state *sstate) { Py_ssize_t find; Py_ssize_t parts_len; @@ -397,7 +398,7 @@ PyObject * fast_unquote(PyObject *self, PyObject *s) { - return _fast_unquote(self, s, -1, NULL); + return _fast_unquote(s, -1, self, NULL); } @@ -499,7 +500,7 @@ } for (i=0; i < parts_len; i++) { o = PyList_GetItem(parts, i); /* borrowed */ - u = _fast_unquote(self, o, -1, sstate); + u = _fast_unquote(o, -1, NULL, sstate); if (u == NULL) { goto error; } @@ -518,6 +519,122 @@ static PyObject * +_fast_split_filters(PyObject *varname, PyObject *self, struct speedups_state *sstate) +{ + Py_ssize_t varname_len; + Py_ssize_t sep; + PyObject *res = NULL; + PyObject *filters = NULL; + PyObject *name = NULL; + PyObject *tmp; + + varname_len = PyUnicode_GetLength(varname); + if (varname_len < 0) { + return NULL; + } + if (varname_len == 0) { + sep = -1; + } + else { + sep = PyUnicode_FindChar(varname, '|', 0, varname_len, 1); + if (sep == -2) { + return NULL; + } + } + if (sep == -1) { + res = PyTuple_New(2); + if (res == NULL) { + goto error; + } + Py_INCREF(varname); /* because PyTuple_SetItem steals */ + PyTuple_SetItem(res, 0, varname); /* steals */ + filters = PyList_New(0); + if (filters == NULL) { + goto error; + } + PyTuple_SetItem(res, 1, filters); /* steals */ + return res; + } + name = PyUnicode_Substring(varname, 0, sep); + if (name == NULL) { + goto error; + } + tmp = PyObject_CallMethod(name, "rstrip", NULL); + if (tmp == NULL) { + goto error; + } + Py_DECREF(name); + name = tmp; + + filters = PyUnicode_Substring(varname, sep+1, varname_len); + if (filters == NULL) { + goto error; + } + tmp = PyObject_CallMethod(filters, "strip", NULL); + if (tmp == NULL) { + goto error; + } + Py_DECREF(filters); + filters = tmp; + + if (PyObject_Not(filters)) { + Py_DECREF(filters); filters = NULL; + + res = PyTuple_New(2); + if (res == NULL) { + goto error; + } + PyTuple_SetItem(res, 0, name); /* steals */ + name = NULL; /* no ownership any more */ + filters = PyList_New(0); + if (filters == NULL) { + goto error; + } + PyTuple_SetItem(res, 1, filters); /* steals */ + return res; + } + + if (sstate == NULL) { + sstate = PyModule_GetState(self); + if (sstate == NULL) { + PyErr_SetString(PyExc_RuntimeError, "no module state available"); + goto error; + } + } + + tmp = PyUnicode_Split(filters, sstate->FILTER_SEPARATOR, -1); + if (tmp == NULL) { + goto error; + } + Py_DECREF(filters); + filters = tmp; + + res = PyTuple_New(2); + if (res == NULL) { + goto error; + } + PyTuple_SetItem(res, 0, name); /* steals -- ownership changed */ + PyTuple_SetItem(res, 1, filters); /* steals -- ownership changed */ + return res; + +error: + Py_XDECREF(res); + Py_XDECREF(filters); + Py_XDECREF(name); + return NULL; +} + + +static +PyObject * +fast_split_filters(PyObject *self, PyObject *varname) +{ + return _fast_split_filters(varname, self, NULL); +} + + +static +PyObject * fast_split_ns(PyObject *self, PyObject *varname) { PyObject *res = NULL; @@ -555,7 +672,7 @@ Py_DECREF(res); return NULL; } - o2 = _fast_unquote(self, o1, ns_idx, NULL); + o2 = _fast_unquote(o1, ns_idx, self, NULL); if (o2 == NULL) { Py_DECREF(o1); Py_DECREF(res); @@ -577,6 +694,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_ns", fast_split_ns, METH_O, PyDoc_STR("C-implementation of configmix.config._split_ns")}, {NULL, NULL, 0, NULL} }; @@ -621,6 +739,12 @@ } PyUnicode_InternInPlace(&(sstate->NS_SEPARATOR)); + sstate->FILTER_SEPARATOR = PyUnicode_FromStringAndSize("|", 1); + if (sstate->FILTER_SEPARATOR == NULL) { + return -1; + } + PyUnicode_InternInPlace(&(sstate->FILTER_SEPARATOR)); + sstate->EMPTY_STR = PyUnicode_FromStringAndSize("", 0); if (sstate->EMPTY_STR == NULL) { return -1; @@ -658,6 +782,7 @@ Py_VISIT(sstate->DOT); Py_VISIT(sstate->QUOTE); Py_VISIT(sstate->NS_SEPARATOR); + Py_VISIT(sstate->FILTER_SEPARATOR); Py_VISIT(sstate->EMPTY_STR); Py_VISIT(sstate->QUOTE_MAP); } @@ -675,6 +800,7 @@ Py_CLEAR(sstate->DOT); Py_CLEAR(sstate->QUOTE); Py_CLEAR(sstate->NS_SEPARATOR); + Py_CLEAR(sstate->FILTER_SEPARATOR); Py_CLEAR(sstate->EMPTY_STR); Py_CLEAR(sstate->QUOTE_MAP); }
--- a/configmix/config.py Sun Jan 02 16:05:35 2022 +0100 +++ b/configmix/config.py Sun Jan 02 20:40:09 2022 +0100 @@ -32,12 +32,13 @@ from .constants import REF_NAMESPACE, NONE_FILTER, EMPTY_FILTER try: from ._speedups import (fast_unquote, fast_quote, fast_pathstr2path, - _fast_split_ns) + _fast_split_ns, _fast_split_filters) except ImportError: fast_unquote = None fast_quote = None fast_pathstr2path = None _fast_split_ns = None + _fast_split_filters = None _MARKER = object() @@ -397,7 +398,7 @@ _split_ns = _py_split_ns -def _split_filters(varname): +def _py_split_filters(varname): """Split off the filter part from the `varname` string :type varname: str @@ -418,6 +419,12 @@ return (name, []) +if _fast_split_filters: + _split_filters = _fast_split_filters +else: + _split_filters = _py_split_filters + + class Configuration(CoercingMethodsMixin, _AttributeDict): """The configuration dictionary with attribute support or
--- a/tests/test.py Sun Jan 02 16:05:35 2022 +0100 +++ b/tests/test.py Sun Jan 02 20:40:09 2022 +0100 @@ -1903,6 +1903,24 @@ def test_split_ns_quoting(self): self.assertEqual((u":", u"%x3a"), self.split_ns(u"%x3a:%x3a")) + def test_split_filters_empty(self): + self.assertEqual((u"", []), self.split_filters(u"")) + + def test_split_filters_varname_only(self): + self.assertEqual((u"varname ", []), self.split_filters(u"varname ")) + + def test_split_filters_single_stripping(self): + self.assertEqual((u" the-varname", []), + self.split_filters(u" the-varname | ")) + + def test_split_filters_one(self): + self.assertEqual((u"the-varname", [u"None"]), + self.split_filters(u"the-varname |None")) + + def test_split_filters_many(self): + self.assertEqual((u"the-varname", [u"Empty", u"None"]), + self.split_filters(u"the-varname |Empty|None")) + class T09Parser(_TParserMixin, unittest.TestCase): @@ -1911,6 +1929,7 @@ self.quote = configmix.config.py_quote self.pathstr2path = configmix.config.py_pathstr2path self.split_ns = configmix.config._py_split_ns + self.split_filters = configmix.config._py_split_filters def test_split_ns_wrong_type(self): self.assertRaises( @@ -1939,6 +1958,7 @@ self.quote = configmix.config.fast_quote self.pathstr2path = configmix.config.fast_pathstr2path self.split_ns = configmix.config._fast_split_ns + self.split_filters = configmix.config._fast_split_filters def test_split_ns_wrong_type(self): self.assertRaises(
