Mercurial > hgrepos > Python > libs > ConfigMix
changeset 603:e55a42144ba9
C-implementations for Configuration.getvarl() and Configuration.getvar_s()
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Tue, 11 Jan 2022 02:50:17 +0100 |
| parents | a2fff0d93d83 |
| children | be25b1ae693d |
| files | configmix/_speedups.c configmix/config.py |
| diffstat | 2 files changed, 272 insertions(+), 36 deletions(-) [+] |
line wrap: on
line diff
--- a/configmix/_speedups.c Tue Jan 11 00:52:56 2022 +0100 +++ b/configmix/_speedups.c Tue Jan 11 02:50:17 2022 +0100 @@ -30,11 +30,16 @@ PyObject *EMPTY_STR; PyObject *QUOTE_MAP; PyObject *MISSING; + PyObject *MARKER; PyObject *STARTTOK; PyObject *ENDTOK; + PyObject *REF_NAMESPACE; }; +static PyObject * _fast_getvar_s(PyObject *, PyObject *, PyObject *, PyObject *, struct speedups_state *, int*); + + static int _hex2ucs4(PyObject *s, Py_ssize_t end, Py_UCS4 *result) @@ -835,9 +840,8 @@ filters = Py_NewRef(PyTuple_GetItem(tmp, 1)); py_clear_ref(&tmp); - tmp = PyObject_CallMethod( - config, "_getvar_s_with_cache_info", "O", varname); - if (tmp == NULL) { + varvalue = _fast_getvar_s(config, varname, NULL, self, sstate, &cacheable); + if (varvalue == NULL) { if (PyErr_ExceptionMatches(PyExc_KeyError)) { cacheable = 1; if (PySequence_Contains(filters, sstate->NONE_FILTER) == 1) { @@ -863,18 +867,6 @@ goto error; } } - else { - if (PyTuple_Size(tmp) != 2) { - py_clear_ref(&tmp); - PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected"); - goto error; - } - /* unpack the result */ - /* borrowed -- cannot fail -- but want ownership */ - varvalue = Py_NewRef(PyTuple_GetItem(tmp, 0)); - cacheable = PyObject_IsTrue(PyTuple_GetItem(tmp, 1)); - py_clear_ref(&tmp); - } if (!cacheable) { use_cache = 0; @@ -1065,9 +1057,9 @@ filters = Py_NewRef(PyTuple_GetItem(tmp, 1)); py_clear_ref(&tmp); - tmp = PyObject_CallMethod( - config, "_getvar_s_with_cache_info", "O", varname); - if (tmp == NULL) { + varvalue = _fast_getvar_s(config, varname, NULL, self, sstate, &cacheable); + + if (varvalue == NULL) { if (PyErr_ExceptionMatches(PyExc_KeyError)) { cacheable = 1; if (PySequence_Contains(filters, sstate->NONE_FILTER) == 1) { @@ -1095,18 +1087,6 @@ goto error; } } - else { - if (PyTuple_Size(tmp) != 2) { - py_clear_ref(&tmp); - PyErr_SetString(PyExc_TypeError, "tuple of size 2 expected"); - goto error; - } - /* unpack the result */ - /* borrowed -- but want own */ - varvalue = Py_NewRef(PyTuple_GetItem(tmp, 0)); - cacheable = PyObject_IsTrue(PyTuple_GetItem(tmp, 1)); - py_clear_ref(&tmp); - } if (!cacheable) { use_cache = 0; @@ -1191,6 +1171,205 @@ static PyObject * +_fast_getvarl(PyObject *config, PyObject *path, PyObject *namespace, PyObject *default_, struct speedups_state *sstate) +{ + PyObject *varvalue; + PyObject *lookupfn = NULL; + + if ((namespace == NULL) || PyObject_Not(namespace)) { + lookupfn = PyObject_GetAttrString(config, "_lookupvar"); + if (lookupfn == NULL) { + goto error; + } + } + else { + int ns_equals_ref = PyUnicode_Compare(namespace, sstate->REF_NAMESPACE); + if ((ns_equals_ref < 0) && PyErr_Occurred()) { + return NULL; + } + if (ns_equals_ref == 0) { + lookupfn = PyObject_GetAttrString(config, "_lookupref"); + if (lookupfn == NULL) { + goto error; + } + } + else { + /* lookup_varns */ + /* this is borrowed */ + PyObject *cfg_vars_mod = PyImport_AddModule("configmix.variables"); + if (cfg_vars_mod == NULL) { + goto error; + } + lookupfn = PyObject_CallMethod(cfg_vars_mod, "lookup_varns", "O", namespace); + if (lookupfn == NULL) { + goto handle_possible_keyerror; + } + } + } + varvalue = PyObject_CallObject(lookupfn, path); + if (varvalue == NULL) { + goto handle_possible_keyerror; + } + Py_DECREF(lookupfn); + return varvalue; + +handle_possible_keyerror: + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + if ((default_ == NULL) || py_object_is(default_, sstate->MARKER)) { + PyErr_Format(PyExc_KeyError, "Variable %R not found", path); + /* fall through */ + } + else { + PyErr_Clear(); + Py_XDECREF(lookupfn); + return Py_NewRef(default_); + } + } + /* fall through */ +error: + Py_XDECREF(lookupfn); + return NULL; +} + + +static +PyObject * +fast_getvarl(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"config", "path", "namespace", "default", NULL}; + PyObject *path; + PyObject *config; + PyObject *namespace = NULL; + PyObject *default_ = NULL; + + struct speedups_state *sstate; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO!|$OO", kwlist, &config, &PyTuple_Type, &path, &namespace, &default_)) { + + return NULL; + } + sstate = PyModule_GetState(self); + if (sstate == NULL) { + PyErr_SetString(PyExc_RuntimeError, "no module state available"); + return NULL; + } + return _fast_getvarl(config, path, namespace, default_, sstate); +} + + +/** + * Mixture of py_getvar_s and _py_getvar_s_with_cache_info + */ +static +PyObject * +_fast_getvar_s(PyObject *config, PyObject *varname, PyObject *default_, PyObject *self, struct speedups_state *sstate, int *cacheable) +{ + PyObject *varname_b; /* always borrowed */ + PyObject *namespace_b; /* always borrowed */ + PyObject *splitted = NULL; + PyObject *res = NULL; + PyObject *tmp1; + PyObject *tmp2; + + splitted = fast_split_ns(self, varname); + if (splitted == NULL) { + goto error; + } + namespace_b = PyTuple_GetItem(splitted, 0); /* borrowed */ + varname_b = PyTuple_GetItem(splitted, 1); /* borrowed */ + + if (PyObject_Not(namespace_b)) { + tmp1 = _fast_pathstr2path(varname_b, NULL, sstate); + if (tmp1 == NULL) { + goto error; + } + tmp2 = _fast_getvarl(config, tmp1, NULL, default_, sstate); + if (tmp2 == NULL) { + py_clear_ref(&tmp1); + goto handle_possible_keyerror; + } + py_clear_ref(&tmp1); + res = PyObject_CallMethod(config, "substitute_variables_in_obj", "O", tmp2); + if (res == NULL) { + py_clear_ref(&tmp2); + goto handle_possible_keyerror; + } + py_clear_ref(&tmp2); + /* no namespace -> cacheable */ + *cacheable = 1; + } + else { + tmp1 = PyTuple_New(1); + if (tmp1 == NULL) { + goto error; + } + PyTuple_SetItem(tmp1, 0, Py_NewRef(varname_b)); + tmp2 = _fast_getvarl(config, tmp1, namespace_b, NULL, sstate); + if (tmp2 == NULL) { + py_clear_ref(&tmp1); + goto handle_possible_keyerror; + } + py_clear_ref(&tmp1); + res = PyObject_CallMethod(config, "substitute_variables_in_obj", "O", tmp2); + if (res == NULL) { + py_clear_ref(&tmp2); + goto handle_possible_keyerror; + } + py_clear_ref(&tmp2); + /* results from namespaced lookups are currently not cacheable */ + *cacheable = 0; + } + + /* free splitted last because of using borrowed refs from it */ + Py_DECREF(splitted); + return res; + +handle_possible_keyerror: + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + if ((default_ == NULL) || py_object_is(default_, sstate->MARKER)) { + /* fall through */ + } + else { + PyErr_Clear(); + Py_XDECREF(res); + Py_XDECREF(splitted); + return Py_NewRef(default_); + } + } + /* fall through */ + +error: + Py_XDECREF(res); + Py_XDECREF(splitted); + return NULL; +} + + +static +PyObject * +fast_getvar_s(PyObject *self, PyObject *args) +{ + PyObject *config; + PyObject *varname; + PyObject *default_; + int cacheable; + struct speedups_state *sstate; + + if (!PyArg_UnpackTuple(args, "config", 3, 3, &config, &varname, &default_)) { + return NULL; + } + + sstate = PyModule_GetState(self); + if (sstate == NULL) { + PyErr_SetString(PyExc_RuntimeError, "no module state available"); + return NULL; + } + return _fast_getvar_s(config, varname, default_, self, sstate, &cacheable); +} + + +static +PyObject * sync_MISSING(PyObject *self, PyObject *missing) { struct speedups_state *sstate; @@ -1209,6 +1388,26 @@ } +static +PyObject * +sync_MARKER(PyObject *self, PyObject *marker) +{ + struct speedups_state *sstate; + + sstate = PyModule_GetState(self); + if (sstate == NULL) { + PyErr_SetString(PyExc_RuntimeError, "no module state available"); + return NULL; + } + if (sstate->MARKER != NULL) { + PyErr_SetString(PyExc_RuntimeError, "_MARKER already set"); + return NULL; + } + sstate->MARKER = Py_NewRef(marker); + Py_RETURN_NONE; +} + + static struct PyMethodDef speedups_methods[] = { {"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")}, @@ -1216,8 +1415,10 @@ {"_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")}, {"_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")}, + {"_fast_getvar_s", fast_getvar_s, METH_VARARGS, PyDoc_STR("C-Implementation of configmix.config.Configuration.getvar_s")}, {"_sync_MISSING", sync_MISSING, METH_O, PyDoc_STR("Internal function to easily sync the _MISSING object with configmix.config")}, - + {"_sync_MARKER", sync_MARKER, METH_O, PyDoc_STR("Internal function to easily sync the _MARKER object with configmix.config")}, {NULL, NULL, 0, NULL} }; @@ -1314,6 +1515,12 @@ } PyUnicode_InternInPlace(&(sstate->ENDTOK)); + sstate->REF_NAMESPACE = PyUnicode_FromStringAndSize("ref", 3); + if (sstate->REF_NAMESPACE == NULL) { + return -1; + } + PyUnicode_InternInPlace(&(sstate->REF_NAMESPACE)); + return 0; } @@ -1334,8 +1541,10 @@ Py_VISIT(sstate->EMPTY_STR); Py_VISIT(sstate->QUOTE_MAP); Py_VISIT(sstate->MISSING); + Py_VISIT(sstate->MARKER); Py_VISIT(sstate->STARTTOK); Py_VISIT(sstate->ENDTOK); + Py_VISIT(sstate->REF_NAMESPACE); } return 0; } @@ -1357,8 +1566,10 @@ Py_CLEAR(sstate->EMPTY_STR); Py_CLEAR(sstate->QUOTE_MAP); Py_CLEAR(sstate->MISSING); + Py_CLEAR(sstate->MARKER); Py_CLEAR(sstate->STARTTOK); Py_CLEAR(sstate->ENDTOK); + Py_CLEAR(sstate->REF_NAMESPACE); } return 0; }
--- a/configmix/config.py Tue Jan 11 00:52:56 2022 +0100 +++ b/configmix/config.py Tue Jan 11 02:50:17 2022 +0100 @@ -33,14 +33,17 @@ try: from ._speedups import (fast_unquote, fast_quote, fast_pathstr2path, _fast_split_ns, _fast_split_filters, + _fast_getvarl, _fast_getvar_s, _fast_interpolate_variables, - _sync_MISSING) + _sync_MISSING, _sync_MARKER) except ImportError: fast_unquote = None fast_quote = None fast_pathstr2path = None _fast_split_ns = None _fast_split_filters = None + _fast_getvarl = None + _fast_getvar_s = None _fast_interpolate_variables = None _sync_MISSING = None @@ -503,7 +506,7 @@ for k in self: yield self.getitem_ns(k) - def getvarl(self, *path, **kwds): + def py_getvarl(self, *path, **kwds): """Get a variable where the hierarchy is given in `path` as sequence and the namespace is given in the `namespace` keyword argument. @@ -531,6 +534,16 @@ else: return varvalue + if _fast_getvarl: + + def fast_getvarl(self, *args, **kwds): + return _fast_getvarl(self, args, **kwds) + + getvarl = fast_getvarl + + else: + getvarl = py_getvarl + def getkeysl(self, *path, **kwds): """Yield the keys of a variable value. @@ -709,7 +722,7 @@ else: return default - def getvar_s(self, varname, default=_MARKER): + def py_getvar_s(self, varname, default=_MARKER): """Get a variable - including variables from other namespaces. `varname` is interpreted as in :meth:`.getvar`. But variables @@ -733,7 +746,18 @@ else: return default - def _getvar_s_with_cache_info(self, varname): + if _fast_getvar_s: + + def fast_getvar_s(self, varname, default=_MARKER): + return _fast_getvar_s(self, varname, default) + + getvar_s = fast_getvar_s + + else: + + getvar_s = py_getvar_s + + def _py_getvar_s_with_cache_info(self, varname): """Internal variant of :meth:`~.getvar_s` that returns some information whether caching of interpolated values is allowed @@ -909,7 +933,7 @@ varname, filters = _split_filters( s[start+2:end]) # noqa: E226 try: - varvalue, cacheable = self._getvar_s_with_cache_info(varname) + varvalue, cacheable = self._py_getvar_s_with_cache_info(varname) except KeyError: cacheable = True if NONE_FILTER in filters: @@ -1241,3 +1265,4 @@ if _sync_MISSING: _sync_MISSING(_MISSING) + _sync_MARKER(_MARKER)
