Rewrote the basic section of the chapter on defining new types.
Changed the example to show how to create types the new way:
- Use a class new method rather than a new function.
- Use self->ob_type->tp_free in deallocators
- Use attribute descriptors rather than set/getattr methods.
- Make the type usable as a base type.
I split the example into 3 parts:
1. The minimal new type
2. Adding attributes and methods.
3. Finer control over attributes.
It's much simpler to define builtin types. These updates hopefully
show this.
I also made minor wording changes in two other places.
I still need to update xxobject.c
diff --git a/Doc/ext/newtypes.tex b/Doc/ext/newtypes.tex
index 631a37a..3948993 100644
--- a/Doc/ext/newtypes.tex
+++ b/Doc/ext/newtypes.tex
@@ -2,6 +2,7 @@
\label{defining-new-types}}
\sectionauthor{Michael Hudson}{mwh@python.net}
\sectionauthor{Dave Kuhlman}{dkuhlman@rexx.com}
+\sectionauthor{Jim Fulton}{jim@zope.com}
As mentioned in the last chapter, Python allows the writer of an
extension module to define new types that can be manipulated from
@@ -38,15 +39,6 @@
The first bit that will be new is:
\begin{verbatim}
-static PyTypeObject noddy_NoddyType;
-\end{verbatim}
-
-This names the type object that will be defining further down in the
-file. It can't be defined here because its definition has to refer to
-functions that have not yet been defined, but we need to be able to
-refer to it, hence the declaration.
-
-\begin{verbatim}
typedef struct {
PyObject_HEAD
} noddy_NoddyObject;
@@ -73,105 +65,54 @@
} PyIntObject;
\end{verbatim}
-Next up is:
-
-\begin{verbatim}
-static PyObject*
-noddy_new_noddy(PyObject* self, PyObject* args)
-{
- noddy_NoddyObject* noddy;
-
- if (!PyArg_ParseTuple(args,":new_noddy"))
- return NULL;
-
- noddy = PyObject_New(noddy_NoddyObject, &noddy_NoddyType);
-
- return (PyObject*)noddy;
-}
-\end{verbatim}
-
-This is in fact just a regular module function, as described in the
-last chapter. The reason it gets special mention is that this is
-where we create our Noddy object. Defining \ctype{PyTypeObject}
-structures is all very well, but if there's no way to actually
-\emph{create} one of the wretched things it is not going to do anyone
-much good.
-
-Almost always, you create objects with a call of the form:
-
-\begin{verbatim}
-PyObject_New(<type>, &<type object>);
-\end{verbatim}
-
-This allocates the memory and then initializes the object (sets
-the reference count to one, makes the \member{ob_type} pointer point at
-the right place and maybe some other stuff, depending on build options).
-You \emph{can} do these steps separately if you have some reason to
---- but at this level we don't bother.
-
-Note that \cfunction{PyObject_New()} is a polymorphic macro rather
-than a real function. The first parameter is the name of the C
-structure that represents an object of our new type, and the return
-value is a pointer to that type. This would be
-\ctype{noddy_NoddyObject} in our example:
-
-\begin{verbatim}
- noddy_NoddyObject *my_noddy;
-
- my_noddy = PyObject_New(noddy_NoddyObject, &noddy_NoddyType);
-\end{verbatim}
-
-We cast the return value to a \ctype{PyObject*} because that's what
-the Python runtime expects. This is safe because of guarantees about
-the layout of structures in the C standard, and is a fairly common C
-programming trick. One could declare \cfunction{noddy_new_noddy} to
-return a \ctype{noddy_NoddyObject*} and then put a cast in the
-definition of \cdata{noddy_methods} further down the file --- it
-doesn't make much difference.
-
-Now a Noddy object doesn't do very much and so doesn't need to
-implement many type methods. One you can't avoid is handling
-deallocation, so we find
-
-\begin{verbatim}
-static void
-noddy_noddy_dealloc(PyObject* self)
-{
- PyObject_Del(self);
-}
-\end{verbatim}
-
-This is so short as to be self explanatory. This function will be
-called when the reference count on a Noddy object reaches \code{0} (or
-it is found as part of an unreachable cycle by the cyclic garbage
-collector). \cfunction{PyObject_Del()} is what you call when you want
-an object to go away. If a Noddy object held references to other
-Python objects, one would decref them here.
-
Moving on, we come to the crunch --- the type object.
\begin{verbatim}
static PyTypeObject noddy_NoddyType = {
PyObject_HEAD_INIT(NULL)
- 0, /* ob_size */
- "Noddy", /* tp_name */
- sizeof(noddy_NoddyObject), /* tp_basicsize */
- 0, /* tp_itemsize */
- noddy_noddy_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
+ 0, /*ob_size*/
+ "noddy.Noddy", /*tp_name*/
+ sizeof(noddy_NoddyObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ "Noddy objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ PyType_GenericNew, /* tp_new */
};
\end{verbatim}
Now if you go and look up the definition of \ctype{PyTypeObject} in
-\file{object.h} you'll see that it has many, many more fields that the
+\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.
@@ -190,9 +131,8 @@
\end{verbatim}
as the type of a type object is ``type'', but this isn't strictly
-conforming C and some compilers complain. So instead we fill in the
-\member{ob_type} field of \cdata{noddy_NoddyType} at the earliest
-oppourtunity --- in \cfunction{initnoddy()}.
+conforming C and some compilers complain. Fortunately, this member
+will be filled in for us by \cfunction{PyType_Ready()}.
\begin{verbatim}
0, /* ob_size */
@@ -204,7 +144,7 @@
versions of Python. Always set this field to zero.
\begin{verbatim}
- "Noddy", /* tp_name */
+ "noddy.Noddy", /* tp_name */
\end{verbatim}
The name of our type. This will appear in the default textual
@@ -214,9 +154,14 @@
>>> "" + noddy.new_noddy()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
-TypeError: cannot add type "Noddy" to string
+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
+\module{noddy} and the type is \class{Noddy}, so we set the type name
+to \class{noddy.Noddy}.
+
\begin{verbatim}
sizeof(noddy_NoddyObject), /* tp_basicsize */
\end{verbatim}
@@ -231,37 +176,70 @@
This has to do with variable length objects like lists and strings.
Ignore this for now.
-Now we get into the type methods, the things that make your objects
-different from the others. Of course, the Noddy object doesn't
-implement many of these, but as mentioned above you have to implement
-the deallocation function.
+Skipping a number of type methods that we don't provide, we set the
+class flags to \constant{Py_TPFLAGS_DEFAULT}.
\begin{verbatim}
- noddy_noddy_dealloc, /* tp_dealloc */
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
\end{verbatim}
-From here, all the type methods are \NULL, so we'll go over them later
---- that's for the next section!
+All types should include this constant in their flags. It enables all
+of the members defined by the current version of Python.
-Everything else in the file should be familiar, except for this line
+We provide a doc string for the type in \member{tp_doc}.
+
+\begin{verbatim}
+ "Noddy objects", /* tp_doc */
+\end{verbatim}
+
+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.
+
+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
+\member{tp_new} implementation. In this case, we can just use the
+default implementation provided by the API function
+\cfunction{PyType_GenericNew}.
+
+\begin{verbatim}
+ PyType_GenericNew, /* tp_new */
+\end{verbatim}
+
+All the other type methods are \NULL, so we'll go over them later
+--- that's for a later section!
+
+Everything else in the file should be familiar, except for some code
in \cfunction{initnoddy}:
\begin{verbatim}
- noddy_NoddyType.ob_type = &PyType_Type;
+ if (PyType_Ready(&noddy_NoddyType) < 0)
+ return;
\end{verbatim}
-This was alluded to above --- the \cdata{noddy_NoddyType} object should
-have type ``type'', but \code{\&PyType_Type} is not constant and so
-can't be used in its initializer. To work around this, we patch it up
-in the module initialization.
+This initializes the \class{Noddy} type, filing in a number of
+members, including \member{ob_type} that we initially set to \NULL.
+
+\begin{verbatim}
+ PyModule_AddObject(m, "Noddy", (PyObject *)&noddy_NoddyType);
+\end{verbatim}
+
+This adds the type to the module dictionary. This allows us to create
+\class{Noddy} instances by calling the \class{Noddy} class:
+
+\begin{verbatim}
+import noddy
+mynoddy = noddy.Noddy()
+\end{verbatim}
That's it! All that remains is to build it; put the above code in a
-file called \file{noddymodule.c} and
+file called \file{noddy.c} and
\begin{verbatim}
from distutils.core import setup, Extension
setup(name="noddy", version="1.0",
- ext_modules=[Extension("noddy", ["noddymodule.c"])])
+ ext_modules=[Extension("noddy", ["noddy.c"])])
\end{verbatim}
in a file called \file{setup.py}; then typing
@@ -276,6 +254,424 @@
That wasn't so hard, was it?
+Of course, the current Noddy type is pretty uninteresting. It has no
+data and doesn't do anything. It can't even be subclasses.
+
+\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:
+
+\verbatiminput{noddy2.c}
+
+This version of the module has a number of changes.
+
+We've added an extra include:
+
+\begin{verbatim}
+#include "structmember.h"
+\end{verbatim}
+
+This include provides declarations that we use to handle attributes,
+as described a bit later.
+
+The name of the \class{Noddy} object structure has been shortened to
+\class{Noddy}. The type object name has been shortened to
+\class{NoddyType}.
+
+The \class{Noddy} type now has three data attributes, \var{first},
+\var{last}, and \var{number}. The \var{first} and \var{last}
+variables are Python strings containing first and last names. The
+\var{number} attribute is an integer.
+
+The object structure is updated accordingly:
+
+\begin{verbatim}
+typedef struct {
+ PyObject_HEAD
+ PyObject *first;
+ PyObject *last;
+ int number;
+} Noddy;
+\end{verbatim}
+
+Because we now have data to manage, we have to be more careful about
+object allocation and deallocation. At a minimum, we need a
+deallocation method:
+
+\begin{verbatim}
+static void
+Noddy_dealloc(Noddy* self)
+{
+ Py_XDECREF(self->first);
+ Py_XDECREF(self->last);
+ self->ob_type->tp_free(self);
+}
+\end{verbatim}
+
+which is assigned to the \member{tp_dealloc} member:
+
+\begin{verbatim}
+ (destructor)Noddy_dealloc, /*tp_dealloc*/
+\end{verbatim}
+
+This method decrements the reference counts of the two Python
+attributes. We use \cfunction{Py_XDECREF} here because the
+\member{first} and \member{last} members could be \NULL. It then
+calls the \member{tp_free} member of the object's type to free the
+object's memory. Note that the object's type might not be
+\class{NoddyType}, because the object may be an instance of a
+subclass.
+
+We want to make sure that the first and last names are initialized to
+empty strings, so we provide a new method:
+
+\begin{verbatim}
+static PyObject *
+Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ Noddy *self;
+
+ self = (Noddy *)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ self->first = PyString_FromString("");
+ if (self->first == NULL)
+ {
+ Py_DECREF(self);
+ return NULL;
+ }
+
+ self->last = PyString_FromString("");
+ if (self->last == NULL)
+ {
+ Py_DECREF(self);
+ return NULL;
+ }
+
+ self->number = 0;
+ }
+
+ return (PyObject *)self;
+}
+\end{verbatim}
+
+and install it in the \member{tp_new} member:
+
+\begin{verbatim}
+ Noddy_new, /* tp_new */
+\end{verbatim}
+
+The new member is responsible for creating (as opposed to
+initializing) objects of the type. It is exposed in Python as the
+\method{__new__} method. See the paper titled ``Unifying types and
+classes in Python'' for a detailed discussion of the \method{__new__}
+method. One reason to implement a new method is to assure the initial
+values of instance variables. In this case, we use the new method to
+make sure that the initial values of the members \member{first} and
+\member{last} are not \NULL. If we didn't care whether the initial
+values were \NULL, we could have used \cfunction{PyType_GenericNew} as
+our new method, as we did before. \cfunction{PyType_GenericNew}
+initializes all of the instance variable members to NULLs.
+
+The new method is a static method that is passed the type being
+instantiated and any arguments passed when the type was called,
+and that returns the new object created. New methods always accept
+positional and keyword arguments, but they often ignore the arguments,
+leaving the argument handling to initializer methods. Note that if the
+type supports subclassing, the type passed may not be the type being
+defined. The new method calls the tp_alloc slot to allocate memory.
+We don't fill the \member{tp_alloc} slot ourselves. Rather
+\cfunction{PyType_Ready()} fills it for us by inheriting it from our
+base class, which is \class{object} by default. Most types use the
+default allocation.
+
+We provide an initialization function:
+
+\begin{verbatim}
+static PyObject *
+Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *first=NULL, *last=NULL;
+
+ static char *kwlist[] = {"first", "last", "number", NULL};
+
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
+ &first, &last,
+ &self->number))
+ return NULL;
+
+ if (first) {
+ Py_XDECREF(self->first);
+ Py_INCREF(first);
+ self->first = first;
+ }
+
+ if (last) {
+ Py_XDECREF(self->last);
+ Py_INCREF(last);
+ self->last = last;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+\end{verbatim}
+
+by filling the \member{tp_init} slot.
+
+\begin{verbatim}
+ (initproc)Noddy_init, /* tp_init */
+\end{verbatim}
+
+The \member{tp_init} slot is exposed in Python as the
+\method{__init__} method. It is used to initialize an object after
+it's created. Unlike the new method, we can't guarantee that the
+initializer is called. The initializer isn't called when unpickling
+objects and it can be overridden. Our initializer accepts arguments
+to provide initial values for our instance. Initializers always accept
+positional and keyword arguments.
+
+We want to want to expose our instance variables as attributes. There
+are a number of ways to do that. The simplest way is to define member
+definitions:
+
+\begin{verbatim}
+static PyMemberDef Noddy_members[] = {
+ {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,
+ "first name"},
+ {"last", T_OBJECT_EX, offsetof(Noddy, last), 0,
+ "last name"},
+ {"number", T_INT, offsetof(Noddy, number), 0,
+ "noddy number"},
+ {NULL} /* Sentinel */
+};
+\end{verbatim}
+
+and put the definitions in the \member{tp_members} slot:
+
+\begin{verbatim}
+ Noddy_members, /* tp_members */
+\end{verbatim}
+
+Each member definition has a member name, type, offset, access flags
+and documentation string. See the ``Generic Attribute Management''
+section below for details.
+
+A disadvantage of this approach is that it doesn't provide a way to
+restrict the types of objects that can be assigned to the Python
+attributes. We expect the first and last names to be strings, but any
+Python objects can be assigned. Further, the attributes can be
+deleted, setting the C pointers to \NULL. Even though we can make
+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.
+
+\begin{verbatim}
+static PyObject *
+Noddy_name(Noddy* self)
+{
+ static PyObject *format = NULL;
+ PyObject *args, *result;
+
+ if (format == NULL) {
+ format = PyString_FromString("%s %s");
+ if (format == NULL)
+ return NULL;
+ }
+
+ if (self->first == NULL) {
+ PyErr_SetString(PyExc_AttributeError, "first");
+ return NULL;
+ }
+
+ if (self->last == NULL) {
+ PyErr_SetString(PyExc_AttributeError, "last");
+ return NULL;
+ }
+
+ args = Py_BuildValue("OO", self->first, self->last);
+ if (args == NULL)
+ return NULL;
+
+ result = PyString_Format(format, args);
+ Py_DECREF(args);
+
+ return result;
+}
+\end{verbatim}
+
+The method is implemented as a C function that takes a \class{Noddy} (or
+\class{Noddy} subclass) instance as the first argument. Methods
+always take an instance as the first argument. Methods often take
+positional and keyword arguments as well, but in this cased we don't
+take any and don't need to accept a positional argument tuple or
+keyword argument dictionary. This method is equivalent to the Python
+method:
+
+\begin{verbatim}
+ def name(self):
+ return "%s %s" % (self.first, self.last)
+\end{verbatim}
+
+Note that we have to check for the possibility that our \member{first}
+and \member{last} members are \NULL. This is because they can be
+deleted, in which case they are set to \NULL. It would be better to
+prevent deletion of these attributes and to restrict the attribute
+values to be strings. We'll see how to do that in the next section.
+
+Now that we've defined the method, we need to create an array of
+method definitions:
+
+\begin{verbatim}
+static PyMethodDef Noddy_methods[] = {
+ {"name", (PyCFunction)Noddy_name, METH_NOARGS,
+ "Return the name, combining the first and last name"
+ },
+ {NULL} /* Sentinel */
+};
+\end{verbatim}
+
+and assign them to the \member{tp_methods} slot:
+
+\begin{verbatim}
+ Noddy_methods, /* tp_methods */
+\end{verbatim}
+
+Note that used the \constant{METH_NOARGS} flag to indicate that the
+method is passed no arguments.
+
+Finally, we'll make our type usable as a base class. We've written
+our methods carefully so far so that they don't make any assumptions
+about the type of the object being created or used, so all we need to
+do is to add the \constant{Py_TPFLAGS_BASETYPE} to our class flag
+definition:
+
+\begin{verbatim}
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+\end{verbatim}
+
+We rename \cfunction{initnoddy} to \cfunction{initnoddy2}
+and update the module name passed to \cfunction{Py_InitModule3}.
+
+Finally, we update our \file{setup.py} file to build the new module:
+
+\begin{verbatim}
+from distutils.core import setup, Extension
+setup(name="noddy", version="1.0",
+ ext_modules=[
+ Extension("noddy", ["noddy.c"]),
+ Extension("noddy2", ["noddy2.c"]),
+ ])
+\end{verbatim}
+
+\subsection{Providing finer control over data attributes}
+
+In this section, we'll provide finer control over how the
+\member{first} and \member{last} attributes are set in the
+\class{Noddy} example. In the previous version of our module, the
+instance variables \member{first} and \member{last} could be set to
+non-string values or even deleted. We want to make sure that these
+attributes always contain strings.
+
+\verbatiminput{noddy3.c}
+
+To provide greater control, over the \member{first} and \member{last}
+attributes, we'll use custom getter and setter functions. Here are
+the functions for getting and setting the \member{first} attribute:
+
+\begin{verbatim}
+Noddy_getfirst(Noddy *self, void *closure)
+{
+ Py_INCREF(self->first);
+ return self->first;
+}
+
+static int
+Noddy_setfirst(Noddy *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
+ return -1;
+ }
+
+ if (! PyString_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
+ "The first attribute value must be a string");
+ return -1;
+ }
+
+ Py_DECREF(self->first);
+ Py_INCREF(value);
+ self->first = value;
+
+ return 0;
+}
+\end{verbatim}
+
+The getter function is passed a \class{Noddy} object and a
+``closure'', which is void pointer. In this case, the closure is
+ignored. (The closure supports an advanced usage in which definition
+data is passed to the getter and setter. This could, for example, be
+used to allow a single set of getter and setter functions that decide
+the attribute to get or set based on data in the closure.)
+
+The setter function is passed the \class{Noddy} object, the new value,
+and the closure. The new value may be \NULL, in which case the
+attribute is being deleted. In our setter, we raise an error if the
+attribute is deleted or if the attribute value is not a string.
+
+We create an array of \ctype{PyGetSetDef} structures:
+
+\begin{verbatim}
+static PyGetSetDef Noddy_getseters[] = {
+ {"first",
+ (getter)Noddy_getfirst, (setter)Noddy_setfirst,
+ "first name",
+ NULL},
+ {"last",
+ (getter)Noddy_getlast, (setter)Noddy_setlast,
+ "last name",
+ NULL},
+ {NULL} /* Sentinel */
+};
+\end{verbatim}
+
+and register it in the \member{tp_getset} slot:
+
+\begin{verbatim}
+ Noddy_getseters, /* tp_getset */
+\end{verbatim}
+
+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
+pass \NULL.
+
+We also remove the member definitions for these attributes:
+
+\begin{verbatim}
+static PyMemberDef Noddy_members[] = {
+ {"number", T_INT, offsetof(Noddy, number), 0,
+ "noddy number"},
+ {NULL} /* Sentinel */
+};
+\end{verbatim}
+
+With these changes, we can assure that the \member{first} and
+\member{last} members are never NULL so we can remove checks for \NULL
+values in almost all cases. This means that most of the
+\cfunction{Py_XDECREF} calls can be converted to \cfunction{Py_DECREF}
+calls. The only place we can't change these calls is in the
+deallocator, where there is the possibility that the initialization of
+these members failed in the constructor.
+
+We also rename the module initialization function and module name in
+the initialization function, as we did before, and we add an extra
+definition to the \file{setup.py} file.
\section{Type Methods
\label{dnt-type-methods}}
@@ -353,7 +749,7 @@
newdatatype_dealloc(newdatatypeobject * obj)
{
free(obj->obj_UnderlyingDatatypePtr);
- PyObject_DEL(obj);
+ obj->ob_type->tp_free(self);
}
\end{verbatim}
@@ -396,7 +792,7 @@
Py_DECREF(self->my_callback);
}
- PyObject_DEL(obj);
+ obj->ob_type->tp_free(self);
}
\end{verbatim}
@@ -716,7 +1112,7 @@
\end{verbatim}
The \member{tp_compare} handler is called when comparisons are needed
-are the object does not implement the specific rich comparison method
+and the object does not implement the specific rich comparison method
which matches the requested comparison. (It is always used if defined
and the \cfunction{PyObject_Compare()} or \cfunction{PyObject_Cmp()}
functions are used, or if \function{cmp()} is used from Python.)
@@ -772,10 +1168,12 @@
beginning. Other protocols have been added over time. For protocols
which depend on several handler routines from the type implementation,
the older protocols have been defined as optional blocks of handlers
-referenced by the type object, while newer protocols have been added
-using additional slots in the main type object, with a flag bit being
-set to indicate that the slots are present. (The flag bit does not
-indicate that the slot values are non-\NULL.)
+referenced by the type object. For newer protocols there are
+additional slots in the main type object, with a flag bit being set to
+indicate that the slots are present and should be checked by the
+interpreter. (The flag bit does not indicate that the slot values are
+non-\NULL. The flag may be set to indicate the presense of a slot,
+but a slot may still be unfilled.)
\begin{verbatim}
PyNumberMethods tp_as_number;
diff --git a/Doc/ext/noddy2.c b/Doc/ext/noddy2.c
new file mode 100644
index 0000000..0408cf4
--- /dev/null
+++ b/Doc/ext/noddy2.c
@@ -0,0 +1,185 @@
+#include <Python.h>
+#include "structmember.h"
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *first;
+ PyObject *last;
+ int number;
+} Noddy;
+
+static void
+Noddy_dealloc(Noddy* self)
+{
+ Py_XDECREF(self->first);
+ Py_XDECREF(self->last);
+ self->ob_type->tp_free(self);
+}
+
+static PyObject *
+Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ Noddy *self;
+
+ self = (Noddy *)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ self->first = PyString_FromString("");
+ if (self->first == NULL)
+ {
+ Py_DECREF(self);
+ return NULL;
+ }
+
+ self->last = PyString_FromString("");
+ if (self->last == NULL)
+ {
+ Py_DECREF(self);
+ return NULL;
+ }
+
+ self->number = 0;
+ }
+
+ return (PyObject *)self;
+}
+
+static PyObject *
+Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *first=NULL, *last=NULL;
+
+ static char *kwlist[] = {"first", "last", "number", NULL};
+
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
+ &first, &last,
+ &self->number))
+ return NULL;
+
+ if (first) {
+ Py_XDECREF(self->first);
+ Py_INCREF(first);
+ self->first = first;
+ }
+
+ if (last) {
+ Py_XDECREF(self->last);
+ Py_INCREF(last);
+ self->last = last;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyMemberDef Noddy_members[] = {
+ {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,
+ "first name"},
+ {"last", T_OBJECT_EX, offsetof(Noddy, last), 0,
+ "last name"},
+ {"number", T_INT, offsetof(Noddy, number), 0,
+ "noddy number"},
+ {NULL} /* Sentinel */
+};
+
+static PyObject *
+Noddy_name(Noddy* self)
+{
+ static PyObject *format = NULL;
+ PyObject *args, *result;
+
+ if (format == NULL) {
+ format = PyString_FromString("%s %s");
+ if (format == NULL)
+ return NULL;
+ }
+
+ if (self->first == NULL) {
+ PyErr_SetString(PyExc_AttributeError, "first");
+ return NULL;
+ }
+
+ if (self->last == NULL) {
+ PyErr_SetString(PyExc_AttributeError, "last");
+ return NULL;
+ }
+
+ args = Py_BuildValue("OO", self->first, self->last);
+ if (args == NULL)
+ return NULL;
+
+ result = PyString_Format(format, args);
+ Py_DECREF(args);
+
+ return result;
+}
+
+static PyMethodDef Noddy_methods[] = {
+ {"name", (PyCFunction)Noddy_name, METH_NOARGS,
+ "Return the name, combining the first and last name"
+ },
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject NoddyType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "noddy.Noddy", /*tp_name*/
+ sizeof(Noddy), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)Noddy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "Noddy objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Noddy_methods, /* tp_methods */
+ Noddy_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Noddy_init, /* tp_init */
+ 0, /* tp_alloc */
+ Noddy_new, /* tp_new */
+};
+
+static PyMethodDef module_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyMODINIT_FUNC
+initnoddy2(void)
+{
+ PyObject* m;
+
+ if (PyType_Ready(&NoddyType) < 0)
+ return;
+
+ m = Py_InitModule3("noddy2", module_methods,
+ "Example module that creates an extension type.");
+
+ if (m == NULL)
+ return;
+
+ PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
+}
diff --git a/Doc/ext/noddy3.c b/Doc/ext/noddy3.c
new file mode 100644
index 0000000..f286caf
--- /dev/null
+++ b/Doc/ext/noddy3.c
@@ -0,0 +1,238 @@
+#include <Python.h>
+#include "structmember.h"
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *first;
+ PyObject *last;
+ int number;
+} Noddy;
+
+static void
+Noddy_dealloc(Noddy* self)
+{
+ Py_XDECREF(self->first);
+ Py_XDECREF(self->last);
+ self->ob_type->tp_free(self);
+}
+
+static PyObject *
+Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ Noddy *self;
+
+ self = (Noddy *)type->tp_alloc(type, 0);
+ if (self != NULL) {
+ self->first = PyString_FromString("");
+ if (self->first == NULL)
+ {
+ Py_DECREF(self);
+ return NULL;
+ }
+
+ self->last = PyString_FromString("");
+ if (self->last == NULL)
+ {
+ Py_DECREF(self);
+ return NULL;
+ }
+
+ self->number = 0;
+ }
+
+ return (PyObject *)self;
+}
+
+static PyObject *
+Noddy_init(Noddy *self, PyObject *args, PyObject *kwds)
+{
+ PyObject *first=NULL, *last=NULL;
+
+ static char *kwlist[] = {"first", "last", "number", NULL};
+
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
+ &first, &last,
+ &self->number))
+ return NULL;
+
+ if (first) {
+ Py_DECREF(self->first);
+ Py_INCREF(first);
+ self->first = first;
+ }
+
+ if (last) {
+ Py_DECREF(self->last);
+ Py_INCREF(last);
+ self->last = last;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMemberDef Noddy_members[] = {
+ {"number", T_INT, offsetof(Noddy, number), 0,
+ "noddy number"},
+ {NULL} /* Sentinel */
+};
+
+static PyObject *
+Noddy_getfirst(Noddy *self, void *closure)
+{
+ Py_INCREF(self->first);
+ return self->first;
+}
+
+static int
+Noddy_setfirst(Noddy *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
+ return -1;
+ }
+
+ if (! PyString_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
+ "The first attribute value must be a string");
+ return -1;
+ }
+
+ Py_DECREF(self->first);
+ Py_INCREF(value);
+ self->first = value;
+
+ return 0;
+}
+
+static PyObject *
+Noddy_getlast(Noddy *self, void *closure)
+{
+ Py_INCREF(self->last);
+ return self->last;
+}
+
+static int
+Noddy_setlast(Noddy *self, PyObject *value, void *closure)
+{
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
+ return -1;
+ }
+
+ if (! PyString_Check(value)) {
+ PyErr_SetString(PyExc_TypeError,
+ "The last attribute value must be a string");
+ return -1;
+ }
+
+ Py_DECREF(self->last);
+ Py_INCREF(value);
+ self->last = value;
+
+ return 0;
+}
+
+static PyGetSetDef Noddy_getseters[] = {
+ {"first",
+ (getter)Noddy_getfirst, (setter)Noddy_setfirst,
+ "first name",
+ NULL},
+ {"last",
+ (getter)Noddy_getlast, (setter)Noddy_setlast,
+ "last name",
+ NULL},
+ {NULL} /* Sentinel */
+};
+
+static PyObject *
+Noddy_name(Noddy* self)
+{
+ static PyObject *format = NULL;
+ PyObject *args, *result;
+
+ if (format == NULL) {
+ format = PyString_FromString("%s %s");
+ if (format == NULL)
+ return NULL;
+ }
+
+ args = Py_BuildValue("OO", self->first, self->last);
+ if (args == NULL)
+ return NULL;
+
+ result = PyString_Format(format, args);
+ Py_DECREF(args);
+
+ return result;
+}
+
+static PyMethodDef Noddy_methods[] = {
+ {"name", (PyCFunction)Noddy_name, METH_NOARGS,
+ "Return the name, combining the first and last name"
+ },
+ {NULL} /* Sentinel */
+};
+
+static PyTypeObject NoddyType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /*ob_size*/
+ "noddy.Noddy", /*tp_name*/
+ sizeof(Noddy), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)Noddy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ "Noddy objects", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Noddy_methods, /* tp_methods */
+ Noddy_members, /* tp_members */
+ Noddy_getseters, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Noddy_init, /* tp_init */
+ 0, /* tp_alloc */
+ Noddy_new, /* tp_new */
+};
+
+static PyMethodDef module_methods[] = {
+ {NULL} /* Sentinel */
+};
+
+PyMODINIT_FUNC
+initnoddy3(void)
+{
+ PyObject* m;
+
+ if (PyType_Ready(&NoddyType) < 0)
+ return;
+
+ m = Py_InitModule3("noddy3", module_methods,
+ "Example module that creates an extension type.");
+
+ if (m == NULL)
+ return;
+
+ PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
+}