From eda29306b34303af2b79366c12f54a223029c165 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 15 Jul 2004 04:05:59 +0000 Subject: [PATCH] Formalize that the Py_VISIT macro requires that the tp_traverse implementation it's used in must give its arguments specific names. --- Doc/api/newtypes.tex | 11 +++---- Doc/ext/newtypes.tex | 71 ++++++++++++++++++++++++-------------------- Include/objimpl.h | 6 +++- 3 files changed, 49 insertions(+), 39 deletions(-) diff --git a/Doc/api/newtypes.tex b/Doc/api/newtypes.tex index 2b58cd90cdf..deb4ac11a22 100644 --- a/Doc/api/newtypes.tex +++ b/Doc/api/newtypes.tex @@ -1664,13 +1664,14 @@ The \member{tp_traverse} handler must have the following type: \end{ctypedesc} To simplify writing \member{tp_traverse} handlers, a -\cfunction{Py_VISIT()} is provided: +\cfunction{Py_VISIT()} macro is provided. In order to use this macro, +the \member{tp_traverse} implementation must name its arguments +exactly \var{visit} and \var{arg}: \begin{cfuncdesc}{void}{Py_VISIT}{PyObject *o} - Call the \var{visit} for \var{o} with \var{arg}. If \var{visit} - returns a non-zero value, then return it. Using this macro, - \member{tp_traverse} handlers look like: - + Call the \var{visit} callback, with arguments \var{o} and \var{arg}. + If \var{visit} returns a non-zero value, then return it. Using this + macro, \member{tp_traverse} handlers look like: \begin{verbatim} static int diff --git a/Doc/ext/newtypes.tex b/Doc/ext/newtypes.tex index 616b1b99fe7..68648530051 100644 --- a/Doc/ext/newtypes.tex +++ b/Doc/ext/newtypes.tex @@ -106,7 +106,7 @@ Now if you go and look up the definition of \ctype{PyTypeObject} in \file{object.h} you'll see that it has many more fields that the definition above. The remaining fields will be filled with zeros by the C compiler, and it's common practice to not specify them -explicitly unless you need them. +explicitly unless you need them. This is so important that we're going to pick the top of it apart still further: @@ -149,7 +149,7 @@ TypeError: cannot add type "noddy.Noddy" to string \end{verbatim} Note that the name is a dotted name that includes both the module name -and the name of the type within the module. The module in this case is +and the name of the type within the module. The module in this case is \module{noddy} and the type is \class{Noddy}, so we set the type name to \class{noddy.Noddy}. @@ -180,7 +180,7 @@ This has to do with variable length objects like lists and strings. Ignore this for now. Skipping a number of type methods that we don't provide, we set the -class flags to \constant{Py_TPFLAGS_DEFAULT}. +class flags to \constant{Py_TPFLAGS_DEFAULT}. \begin{verbatim} Py_TPFLAGS_DEFAULT, /*tp_flags*/ @@ -197,8 +197,8 @@ We provide a doc string for the type in \member{tp_doc}. Now we get into the type methods, the things that make your objects different from the others. We aren't going to implement any of these -in this version of the module. We'll expand this example later to -have more interesting behavior. +in this version of the module. We'll expand this example later to +have more interesting behavior. For now, all we want to be able to do is to create new \class{Noddy} objects. To enable object creation, we have to provide a @@ -268,7 +268,7 @@ Of course, the current Noddy type is pretty uninteresting. It has no data and doesn't do anything. It can't even be subclassed. \subsection{Adding data and methods to the Basic example} - + Let's expend the basic example to add some data and methods. Let's also make the type usable as a base class. We'll create a new module, \module{noddy2} that adds these capabilities: @@ -351,7 +351,7 @@ Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds) Py_DECREF(self); return NULL; } - + self->last = PyString_FromString(""); if (self->last == NULL) { @@ -417,10 +417,10 @@ Noddy_init(Noddy *self, PyObject *args, PyObject *kwds) static char *kwlist[] = {"first", "last", "number", NULL}; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, - &first, &last, + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist, + &first, &last, &self->number)) - return -1; + return -1; if (first) { tmp = self->first; @@ -488,7 +488,7 @@ this? garbage collection, there are calls that can be made to ``untrack'' the object from garbage collection, however, these calls are advanced and not covered here.} -\item +\item \end{itemize} @@ -527,7 +527,7 @@ sure the members are initialized to non-\NULL{} values, the members can be set to \NULL{} if the attributes are deleted. We define a single method, \method{name}, that outputs the objects -name as the concatenation of the first and last names. +name as the concatenation of the first and last names. \begin{verbatim} static PyObject * @@ -558,7 +558,7 @@ Noddy_name(Noddy* self) result = PyString_Format(format, args); Py_DECREF(args); - + return result; } \end{verbatim} @@ -656,16 +656,16 @@ Noddy_setfirst(Noddy *self, PyObject *value, void *closure) PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute"); return -1; } - + if (! PyString_Check(value)) { - PyErr_SetString(PyExc_TypeError, + PyErr_SetString(PyExc_TypeError, "The first attribute value must be a string"); return -1; } - + Py_DECREF(self->first); Py_INCREF(value); - self->first = value; + self->first = value; return 0; } @@ -687,11 +687,11 @@ We create an array of \ctype{PyGetSetDef} structures: \begin{verbatim} static PyGetSetDef Noddy_getseters[] = { - {"first", + {"first", (getter)Noddy_getfirst, (setter)Noddy_setfirst, "first name", NULL}, - {"last", + {"last", (getter)Noddy_getlast, (setter)Noddy_setlast, "last name", NULL}, @@ -705,7 +705,7 @@ and register it in the \member{tp_getset} slot: Noddy_getseters, /* tp_getset */ \end{verbatim} -to register out attribute getters and setters. +to register out attribute getters and setters. The last item in a \ctype{PyGetSetDef} structure is the closure mentioned above. In this case, we aren't using the closure, so we just @@ -737,10 +737,10 @@ Noddy_init(Noddy *self, PyObject *args, PyObject *kwds) static char *kwlist[] = {"first", "last", "number", NULL}; - if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist, - &first, &last, + if (! PyArg_ParseTupleAndKeywords(args, kwds, "|SSi", kwlist, + &first, &last, &self->number)) - return -1; + return -1; if (first) { tmp = self->first; @@ -838,11 +838,11 @@ For each subobject that can participate in cycles, we need to call the \cfunction{visit()} function, which is passed to the traversal method. The \cfunction{visit()} function takes as arguments the subobject and the extra argument \var{arg} passed to the traversal method. It -returns an integer value that must be returned if it is non-zero. +returns an integer value that must be returned if it is non-zero. -Python 2.4 and higher provide a \cfunction{Py_VISIT()} that automates -calling visit functions. With \cfunction{Py_VISIT()}, the +Python 2.4 and higher provide a \cfunction{Py_VISIT()} macro that automates +calling visit functions. With \cfunction{Py_VISIT()}, \cfunction{Noddy_traverse()} can be simplified: @@ -856,12 +856,17 @@ Noddy_traverse(Noddy *self, visitproc visit, void *arg) } \end{verbatim} +\note{Note that the \member{tp_traverse} implementation must name its + arguments exactly \var{visit} and \var{arg} in order to use + \cfunction{Py_VISIT()}. This is to encourage uniformity + across these boring implementations.} + We also need to provide a method for clearing any subobjects that can participate in cycles. We implement the method and reimplement the deallocator to use it: \begin{verbatim} -static int +static int Noddy_clear(Noddy *self) { PyObject *tmp; @@ -903,7 +908,7 @@ the careful decrementing of reference counts. With simplified: \begin{verbatim} -static int +static int Noddy_clear(Noddy *self) { Py_CLEAR(self->first); @@ -973,7 +978,7 @@ later. Here you can put a string (or its address) that you want returned when the Python script references \code{obj.__doc__} to retrieve the doc string. - + Now we come to the basic type methods---the ones most extension types will implement. @@ -1281,7 +1286,7 @@ to retrieve the descriptor from the class object, and get the doc string using its \member{__doc__} attribute. As with the \member{tp_methods} table, a sentinel entry with a -\member{name} value of \NULL{} is required. +\member{name} value of \NULL{} is required. % XXX Descriptors need to be explained in more detail somewhere, but @@ -1345,7 +1350,7 @@ instance would be called. When an attribute should be deleted, the third parameter will be \NULL. Here is an example that simply raises an exception; if this were really all you wanted, the \member{tp_setattr} handler should be set to \NULL. - + \begin{verbatim} static int newdatatype_setattr(newdatatypeobject *obj, char *name, PyObject *v) @@ -1389,7 +1394,7 @@ static int newdatatype_compare(newdatatypeobject * obj1, newdatatypeobject * obj2) { long result; - + if (obj1->obj_UnderlyingDatatypePtr->size < obj2->obj_UnderlyingDatatypePtr->size) { result = -1; @@ -1489,7 +1494,7 @@ This function takes three arguments: this is non-\NULL, raise a \exception{TypeError} with a message saying that keyword arguments are not supported. \end{enumerate} - + Here is a desultory example of the implementation of the call function. \begin{verbatim} diff --git a/Include/objimpl.h b/Include/objimpl.h index 649d1bc8b34..5707e500eef 100644 --- a/Include/objimpl.h +++ b/Include/objimpl.h @@ -302,7 +302,11 @@ PyAPI_FUNC(void) PyObject_GC_Del(void *); ( (type *) _PyObject_GC_NewVar((typeobj), (n)) ) -/* Utility macro to help write tp_traverse functions */ +/* Utility macro to help write tp_traverse functions. + * To use this macro, the tp_traverse function must name its arguments + * "visit" and "arg". This is intended to keep tp_traverse functions + * looking as much alike as possible. + */ #define Py_VISIT(op) \ do { \ if (op) { \