diff options
-rw-r--r-- | CHANGES.rst | 4 | ||||
-rw-r--r-- | src/zope/interface/_zope_interface_coptimizations.c | 120 | ||||
-rw-r--r-- | src/zope/interface/adapter.py | 1 |
3 files changed, 125 insertions, 0 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index d0ee2e8..f439476 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -116,6 +116,10 @@ Like the above, this will break consumers depending on the exact output of error messages if more than one error is present. +- Implement ``adapter._lookup`` in C. In a Plone test run measured with + Py-Spy the function is 1.7x faster than before. See `PR xxx + <https://github.com/zopefoundation/zope.interface/pull/xxx>`_. + 4.7.1 (2019-11-11) ================== diff --git a/src/zope/interface/_zope_interface_coptimizations.c b/src/zope/interface/_zope_interface_coptimizations.c index f4f9cce..dc30763 100644 --- a/src/zope/interface/_zope_interface_coptimizations.c +++ b/src/zope/interface/_zope_interface_coptimizations.c @@ -1678,6 +1678,125 @@ static PyTypeObject VerifyingBase = { /* ==================================================================== */ +/* ==================================================================== */ +/* ========================== Begin: _recursive_lookup ================ */ + +// def _lookup(components, specs, provided, name, i, l): +// components_get = components.get +// if i < l: +// for spec in specs[i].__sro__: +// comps = components_get(spec) +// if comps: +// r = _lookup(comps, specs, provided, name, i+1, l) +// if r is not None: +// return r +// else: +// for iface in provided: +// comps = components_get(iface) +// if comps: +// r = comps.get(name) +// if r is not None: +// return r +// return None + + +static PyObject* +_lookup_recursive(PyObject *components, + PyObject *specs, + PyObject *provided, + PyObject *name, + int i, + int l) +{ + /* recursive component lookup + */ + PyObject *result=NULL, + *comps=NULL, + *sro_at_i=NULL, + *key=NULL; + Py_hash_t hash; + PyListObject *provided_list; + int idx, length; + + if (i < l) { + sro_at_i = PyObject_GetAttrString(PyTuple_GetItem(specs, i), "__sro__"); + length = PySequence_Size(sro_at_i); + for (idx = 0; idx < length; idx++) { + key = ((PyTupleObject *)sro_at_i) -> ob_item[idx]; + hash = PyObject_Hash(key); + if (hash != -1) { + comps = _PyDict_GetItem_KnownHash(components, key, hash); + if (comps && comps!=Py_None) { + result = _lookup_recursive(comps, specs, provided, name, i+1, l); + if (result && result!=Py_None) { + return result; + } + } + } + } + } else { + provided_list = (PyListObject *)provided; + length = PySequence_Size(provided); + for (idx = 0; idx < length; idx++) { + comps = PyDict_GetItemWithError(components, provided_list -> ob_item[idx]); + if (comps && comps!=Py_None) { + result = PyDict_GetItem(comps, name); + if (result && result!=Py_None) { + return result; + } + } + } + } + Py_RETURN_NONE; +} + +static PyObject* +_lookup_component(PyObject* self, PyObject *args) +{ + /* Entry to fast recursive component lookup + */ + PyObject *components=NULL, + *specs=NULL, + *provided=NULL, + *name=NULL, + *result=NULL; + int i, l; + + if (!PyArg_ParseTuple(args, "OOOOii", + &components, &specs, &provided, &name, &i, &l)) { + return NULL; + } + if (!PyDict_Check(components)) { + PyErr_SetString(PyExc_TypeError, "Wrong type for 'components'."); + Py_RETURN_NONE; + } + if (!PyTuple_Check(specs)) { + PyErr_SetString(PyExc_TypeError, "Wrong type for 'specs'."); + Py_RETURN_NONE; + } + if (!PyList_Check(provided)) { + PyErr_SetString(PyExc_TypeError, "Wrong type for 'provided'."); + Py_RETURN_NONE; + } +#ifdef PY3K + if ( name && !PyUnicode_Check(name) ) +#else + if ( name && !PyString_Check(name) && !PyUnicode_Check(name) ) +#endif + { + PyErr_SetString(PyExc_TypeError, "Wrong type for 'str'."); + Py_RETURN_NONE; + } + result = _lookup_recursive(components, specs, provided, name, i, l); + // result is a borrowed ref, but we need to take ownership and pass over + // to caller here before return + Py_INCREF(result); + return result; +} + +/* ========================== End: _recursive_lookup ================= */ +/* ==================================================================== */ + static struct PyMethodDef m_methods[] = { {"implementedBy", (PyCFunction)implementedBy, METH_O, @@ -1687,6 +1806,7 @@ static struct PyMethodDef m_methods[] = { "Get an object's interfaces (internal api)"}, {"providedBy", (PyCFunction)providedBy, METH_O, "Get an object's interfaces"}, + { "_lookup", (PyCFunction)_lookup_component, METH_VARARGS, "lookup a component, fast" }, {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */ }; diff --git a/src/zope/interface/adapter.py b/src/zope/interface/adapter.py index 40b2921..71944b4 100644 --- a/src/zope/interface/adapter.py +++ b/src/zope/interface/adapter.py @@ -661,6 +661,7 @@ def _convert_None_to_Interface(x): else: return x +@_use_c_impl def _lookup(components, specs, provided, name, i, l): # this function is called very often. # The components.get in loops is executed 100 of 1000s times. |