summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst4
-rw-r--r--src/zope/interface/_zope_interface_coptimizations.c120
-rw-r--r--src/zope/interface/adapter.py1
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.