diff options
-rw-r--r-- | Doc/api/newtypes.tex | 15 | ||||
-rw-r--r-- | Include/methodobject.h | 7 | ||||
-rw-r--r-- | Misc/NEWS | 6 | ||||
-rw-r--r-- | Objects/dictobject.c | 34 | ||||
-rw-r--r-- | Objects/listobject.c | 5 | ||||
-rw-r--r-- | Objects/methodobject.c | 2 | ||||
-rw-r--r-- | Objects/setobject.c | 26 | ||||
-rw-r--r-- | Objects/typeobject.c | 5 |
8 files changed, 97 insertions, 3 deletions
diff --git a/Doc/api/newtypes.tex b/Doc/api/newtypes.tex index 1fadc46..038cce9 100644 --- a/Doc/api/newtypes.tex +++ b/Doc/api/newtypes.tex @@ -306,6 +306,21 @@ may be set for any given method. \versionadded{2.3} \end{datadesc} +One other constant controls whether a method is loaded in place of +another definition with the same method name. + +\begin{datadesc}{METH_COEXIST} + The method will be loaded in place of existing definitions. Without + \var{METH_COEXIST}, the default is to skip repeated definitions. Since + slot wrappers are loaded before the method table, the existence of a + \var{sq_contains} slot, for example, would generate a wrapped method + named \method{__contains__()} and preclude the loading of a + corresponding PyCFunction with the same name. With the flag defined, + the PyCFunction will be loaded in place of the wrapper object and will + co-exist with the slot. This is helpful because calls to PyCFunctions + are optimized more than wrapper object calls. + \versionadded{2.4} +\end{datadesc} \begin{cfuncdesc}{PyObject*}{Py_FindMethod}{PyMethodDef table[], PyObject *ob, char *name} diff --git a/Include/methodobject.h b/Include/methodobject.h index 887385d..0f60549 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -58,6 +58,13 @@ PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *, #define METH_CLASS 0x0010 #define METH_STATIC 0x0020 +/* METH_COEXIST allows a method to be entered eventhough a slot has + already filled the entry. When defined, the flag allows a separate + method, "__contains__" for example, to coexist with a defined + slot like sq_contains. */ + +#define METH_COEXIST 0x0040 + typedef struct PyMethodChain { PyMethodDef *methods; /* Methods of this type */ struct PyMethodChain *link; /* NULL or base type */ @@ -281,6 +281,12 @@ Build C API ----- +- Created a new method flag, METH_COEXIST, which causes a method to be loaded + even if already defined by a slot wrapper. This allows a __contains__ + method, for example, to co-exist with a defined sq_contains slot. This + is helpful because the PyCFunction can take advantage of optimized calls + whenever METH_O or METH_NOARGS flags are defined. + - Added a new function, PyDict_Contains(d, k) which is like PySequence_Contains() but is specific to dictionaries and executes about 10% faster. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 0cf71b5..013f5f2 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -498,6 +498,31 @@ PyDict_GetItem(PyObject *op, PyObject *key) return (mp->ma_lookup)(mp, key, hash)->me_value; } +static PyObject * +dict_getitem(PyObject *op, PyObject *key) +{ + long hash; + dictobject *mp = (dictobject *)op; + PyObject *v; + + if (!PyDict_Check(op)) { + return NULL; + } + if (!PyString_CheckExact(key) || + (hash = ((PyStringObject *) key)->ob_shash) == -1) + { + hash = PyObject_Hash(key); + if (hash == -1) + return NULL; + } + v = (mp->ma_lookup)(mp, key, hash) -> me_value; + if (v == NULL) + PyErr_SetObject(PyExc_KeyError, key); + else + Py_INCREF(v); + return v; +} + /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the * dictionary if it is merely replacing the value for an existing key. * This is means that it's safe to loop over a dictionary with @@ -1735,6 +1760,11 @@ dict_iteritems(dictobject *dict) PyDoc_STRVAR(has_key__doc__, "D.has_key(k) -> True if D has a key k, else False"); +PyDoc_STRVAR(contains__doc__, +"D.__contains__(k) -> True if D has a key k, else False"); + +PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); + PyDoc_STRVAR(get__doc__, "D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."); @@ -1781,6 +1811,10 @@ PyDoc_STRVAR(iteritems__doc__, "D.iteritems() -> an iterator over the (key, value) items of D"); static PyMethodDef mapp_methods[] = { + {"__contains__",(PyCFunction)dict_has_key, METH_O | METH_COEXIST, + contains__doc__}, + {"__getitem__", (PyCFunction)dict_getitem, METH_O | METH_COEXIST, + getitem__doc__}, {"has_key", (PyCFunction)dict_has_key, METH_O, has_key__doc__}, {"get", (PyCFunction)dict_get, METH_VARARGS, diff --git a/Objects/listobject.c b/Objects/listobject.c index 7d5c8b4..3915cc9 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -2371,6 +2371,8 @@ list_nohash(PyObject *self) static PyObject *list_iter(PyObject *seq); static PyObject *list_reversed(PyListObject* seq, PyObject* unused); +PyDoc_STRVAR(getitem_doc, +"x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(reversed_doc, "L.__reversed__() -- return a reverse iterator over the list"); PyDoc_STRVAR(append_doc, @@ -2396,7 +2398,10 @@ PyDoc_STRVAR(sorted_doc, "list.sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list;\n\ cmp(x, y) -> -1, 0, 1"); +static PyObject *list_subscript(PyListObject*, PyObject*); + static PyMethodDef list_methods[] = { + {"__getitem__", (PyCFunction)list_subscript, METH_O|METH_COEXIST, getitem_doc}, {"__reversed__",(PyCFunction)list_reversed, METH_NOARGS, reversed_doc}, {"append", (PyCFunction)listappend, METH_O, append_doc}, {"insert", (PyCFunction)listinsert, METH_VARARGS, insert_doc}, diff --git a/Objects/methodobject.c b/Objects/methodobject.c index 3a92fa4..5818616 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -67,7 +67,7 @@ PyCFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) PyObject *self = PyCFunction_GET_SELF(func); int size; - switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC)) { + switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)) { case METH_VARARGS: if (kw == NULL || PyDict_Size(kw) == 0) return (*meth)(self, arg); diff --git a/Objects/setobject.c b/Objects/setobject.c index c060077..8e70546 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -156,6 +156,28 @@ set_contains(PySetObject *so, PyObject *key) } static PyObject * +set_direct_contains(PySetObject *so, PyObject *key) +{ + PyObject *tmp; + long result; + + result = PyDict_Contains(so->data, key); + if (result == -1 && PyAnySet_Check(key)) { + PyErr_Clear(); + tmp = frozenset_dict_wrapper(((PySetObject *)(key))->data); + if (tmp == NULL) + return NULL; + result = PyDict_Contains(so->data, tmp); + Py_DECREF(tmp); + } + if (result == -1) + return NULL; + return PyBool_FromLong(result); +} + +PyDoc_STRVAR(contains_doc, "x.__contains__(y) <==> y in x."); + +static PyObject * set_copy(PySetObject *so) { return make_new_set(so->ob_type, (PyObject *)so); @@ -968,6 +990,8 @@ static PyMethodDef set_methods[] = { add_doc}, {"clear", (PyCFunction)set_clear, METH_NOARGS, clear_doc}, + {"__contains__", (PyCFunction)set_direct_contains, METH_O | METH_COEXIST, + contains_doc}, {"copy", (PyCFunction)set_copy, METH_NOARGS, copy_doc}, {"__copy__", (PyCFunction)set_copy, METH_NOARGS, @@ -1094,6 +1118,8 @@ PyTypeObject PySet_Type = { static PyMethodDef frozenset_methods[] = { + {"__contains__", (PyCFunction)set_direct_contains, METH_O | METH_COEXIST, + contains_doc}, {"copy", (PyCFunction)frozenset_copy, METH_NOARGS, copy_doc}, {"__copy__", (PyCFunction)frozenset_copy, METH_NOARGS, diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e4eadb8..545dba6 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2792,8 +2792,9 @@ add_methods(PyTypeObject *type, PyMethodDef *meth) for (; meth->ml_name != NULL; meth++) { PyObject *descr; - if (PyDict_GetItemString(dict, meth->ml_name)) - continue; + if (PyDict_GetItemString(dict, meth->ml_name) && + !(meth->ml_flags & METH_COEXIST)) + continue; if (meth->ml_flags & METH_CLASS) { if (meth->ml_flags & METH_STATIC) { PyErr_SetString(PyExc_ValueError, |