Skip to content

Commit

Permalink
Implement _lookup in C-extension
Browse files Browse the repository at this point in the history
  • Loading branch information
jensens committed Feb 18, 2020
1 parent 7f6f60e commit 92d7201
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Expand Up @@ -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)
==================
Expand Down
120 changes: 120 additions & 0 deletions src/zope/interface/_zope_interface_coptimizations.c
Expand Up @@ -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,
Expand All @@ -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 */
};
Expand Down
1 change: 1 addition & 0 deletions src/zope/interface/adapter.py
Expand Up @@ -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.
Expand Down

0 comments on commit 92d7201

Please sign in to comment.