[Patches] __contains__ hook, done right

Guido van Rossum guido@python.org
Wed, 16 Feb 2000 15:06:47 -0500


Moshe,

I've finally had some time to review your patch.  I think it's close.

> diff -r -c ../../../python/dist/src/Include/object.h ./Include/object.h
> *** ../../../python/dist/src/Include/object.h	Wed Feb  2 16:02:51 2000
> --- ./Include/object.h	Fri Feb  4 13:39:13 2000
> ***************
> *** 151,156 ****
> --- 151,160 ----
>   typedef int (*getsegcountproc) Py_PROTO((PyObject *, int *));
>   typedef int (*getcharbufferproc) Py_PROTO((PyObject *, int, const char **));
>   
> + /* tentative (by Moshe) */

Please don't put comments like this in :-)  (See the patches style
guide: http://www.python.org/patches/style.html).

> + typedef int(*objobjproc) Py_PROTO((PyObject *, PyObject *));
> + 
> + 
>   typedef struct {
>   	binaryfunc nb_add;
>   	binaryfunc nb_subtract;
> ***************
> *** 185,190 ****
> --- 189,195 ----
>   	intintargfunc sq_slice;
>   	intobjargproc sq_ass_item;
>   	intintobjargproc sq_ass_slice;
> + 	objobjproc sq_contains;
>   } PySequenceMethods;
>   
>   typedef struct {
> ***************
> *** 316,321 ****
> --- 321,327 ----
>   
>   /* PyBufferProcs contains bf_getcharbuffer */
>   #define Py_TPFLAGS_HAVE_GETCHARBUFFER  (1L<<0)
> + #define Py_TPFLAGS_HAVE_SEQUENCE_IN    (1L<<1)
>   
>   #define Py_TPFLAGS_DEFAULT  (Py_TPFLAGS_HAVE_GETCHARBUFFER)

I would modify this to include Py_TPFLAGS_HAVE_SEQUENCE_IN by default.
The flag means that the code knows that the sq_contains field exists;
not that this particular object has a non-NULL value in it.  So it can
always be on in code compiled with this version of the header file.

> diff -r -c ../../../python/dist/src/Objects/abstract.c ./Objects/abstract.c
> *** ../../../python/dist/src/Objects/abstract.c	Fri Oct 15 14:09:02 1999
> --- ./Objects/abstract.c	Fri Feb  4 14:00:45 2000
> ***************
> *** 1110,1116 ****
> --- 1110,1121 ----
>   		}
>   		return 0;
>   	}
> + 	if(PyType_HasFeature(w->ob_type, Py_TPFLAGS_HAVE_SEQUENCE_IN)) {
> + 		sq = w->ob_type->tp_as_sequence;

Here you have to check that sq != NULL first.  (This is because I
changed the meaning of the flag!)

> + 		if(sq->sq_contains != NULL)
> + 			return (*sq->sq_contains)(w, v);
>   
> + 	}

I would put the blank line *after* the brace :-)

>   	sq = w->ob_type->tp_as_sequence;
>   	if (sq == NULL || sq->sq_item == NULL) {
>   		PyErr_SetString(PyExc_TypeError,
> diff -r -c ../../../python/dist/src/Objects/classobject.c ./Objects/classobject.c
> *** ../../../python/dist/src/Objects/classobject.c	Wed Feb  2 16:03:19 2000
> --- ./Objects/classobject.c	Fri Feb  4 13:41:03 2000
> ***************
> *** 1065,1070 ****
> --- 1065,1096 ----
>   	return 0;
>   }
>   
> + static int instance_contains(PyInstanceObject *inst, PyObject *member)
> + {
> + 	static PyObject *__contains__;
> + 	PyObject *func, *arg, *res;
> + 	int ret;
> + 
> + 	if(__contains__ == NULL)
> + 		__contains__ = PyString_InternFromString("__contains__");

Wot?  No error check?  What if we run out of memory?  (See examples
using PyString_InternFromString() elsewhere in classobject.c.)

> + 	func = instance_getattr(inst, __contains__);
> + 	if(func == NULL)
> + 		return -1;
> + 	arg = Py_BuildValue("(O)", member);
> + 	if(arg == NULL) {
> + 		Py_DECREF(func);
> + 		return -1;
> + 	}
> + 	res = PyEval_CallObject(func, arg);
> + 	Py_DECREF(func);
> + 	Py_DECREF(arg);
> + 	if(res == NULL)
> + 		return -1;
> + 	ret = PyObject_IsTrue(res);
> + 	Py_DECREF(res);
> + 	return ret;
> + }
> + 

Good!

>   static PySequenceMethods instance_as_sequence = {
>   	(inquiry)instance_length, /*sq_length*/
>   	0, /*sq_concat*/
> ***************
> *** 1073,1078 ****
> --- 1099,1105 ----
>   	(intintargfunc)instance_slice, /*sq_slice*/
>   	(intobjargproc)instance_ass_item, /*sq_ass_item*/
>   	(intintobjargproc)instance_ass_slice, /*sq_ass_slice*/
> + 	(objobjproc)instance_contains, /* sq_contains */
>   };
>   
>   static PyObject *
> ***************
> *** 1405,1410 ****
> --- 1432,1439 ----
>   	0,			/*tp_str*/
>   	(getattrofunc)instance_getattr, /*tp_getattro*/
>   	(setattrofunc)instance_setattr, /*tp_setattro*/
> + 	0,                     /* tp_as_buffer */
> + 	Py_TPFLAGS_HAVE_SEQUENCE_IN, /* tp_flags */

This could be Py_TPFLAGS_DEFAULT.

>   };
>   
>   
>

I was hoping that the special casing for strings, lists and tuples
would also be moved to those respective files, from the code in
abstract.c.

--Guido van Rossum (home page: http://www.python.org/~guido/)