[Python-checkins] python/dist/src/Doc/ext noddy4.c,NONE,1.1 newtypes.tex,1.28,1.29 setup.py,1.1,1.2 test.py,1.1,1.2 cycle-gc.c,1.1,NONE
dcjim@users.sourceforge.net
dcjim@users.sourceforge.net
Sat, 28 Jun 2003 06:29:18 -0700
Update of /cvsroot/python/python/dist/src/Doc/ext
In directory sc8-pr-cvs1:/tmp/cvs-serv29154/ext
Modified Files:
newtypes.tex setup.py test.py
Added Files:
noddy4.c
Removed Files:
cycle-gc.c
Log Message:
Rewrote the docs for supporting cyclic garbage collection to reflect
the new way that once writes types.
Deleted the old section and sample code and added a new section
building on the Noddy example.
--- NEW FILE: noddy4.c ---
#include <Python.h>
#include "structmember.h"
typedef struct {
PyObject_HEAD
PyObject *first;
PyObject *last;
int number;
} Noddy;
static int
Noddy_traverse(Noddy *self, visitproc visit, void *arg)
{
if (self->first && visit(self->first, arg) < 0)
return -1;
if (self->last && visit(self->last, arg) < 0)
return -1;
return 0;
}
static int
Noddy_clear(Noddy *self)
{
Py_XDECREF(self->first);
self->first = NULL;
Py_XDECREF(self->last);
self->last = NULL;
return 0;
}
static void
Noddy_dealloc(Noddy* self)
{
Noddy_clear(self);
self->ob_type->tp_free((PyObject*)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 int
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 -1;
if (first) {
Py_XDECREF(self->first);
Py_INCREF(first);
self->first = first;
}
if (last) {
Py_XDECREF(self->last);
Py_INCREF(last);
self->last = last;
}
return 0;
}
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 | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
"Noddy objects", /* tp_doc */
(traverseproc)Noddy_traverse, /* tp_traverse */
(inquiry)Noddy_clear, /* 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 */
};
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
initnoddy4(void)
{
PyObject* m;
if (PyType_Ready(&NoddyType) < 0)
return;
m = Py_InitModule3("noddy4", module_methods,
"Example module that creates an extension type.");
if (m == NULL)
return;
Py_INCREF(&NoddyType);
PyModule_AddObject(m, "Noddy", (PyObject *)&NoddyType);
}
Index: newtypes.tex
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/ext/newtypes.tex,v
retrieving revision 1.28
retrieving revision 1.29
diff -C2 -d -r1.28 -r1.29
*** newtypes.tex 28 Jun 2003 11:53:12 -0000 1.28
--- newtypes.tex 28 Jun 2003 13:29:16 -0000 1.29
***************
*** 671,674 ****
--- 671,765 ----
definition to the \file{setup.py} file.
+ \subsection{Supporting cyclic garbage collection}
+
+ Python has a cyclic-garbage collector that can identify unneeded
+ objects even when their reference counts are not zero. This can happen
+ when objects are involved in cycles. For example, consider:
+
+ \begin{verbatim}
+ >>> l = []
+ >>> l.append(l)
+ >>> del l
+ \end{verbatim}
+
+ In this example, we create a list that contains itself. When we delete
+ it, it still has a reference from itself. It's reference count doesn't
+ drop to zero. Fortunately, Python's cyclic-garbage collector will
+ eventually figure out that that the list is garbage and free it.
+
+ In the second version of the \class{Noddy} example, we allowed any
+ kind of object to be stored in the \member{first} or \member{last}
+ attributes. This means that \class{Noddy} objects can participate in
+ cycles:
+
+ \begin{verbatim}
+ >>> import noddy2
+ >>> n = noddy2.Noddy()
+ >>> l = [n]
+ >>> n.first = l
+ \end{verbatim}
+
+ This is pretty silly, but it gives us an excuse to add support for the
+ cyclic-garbage collector to the \class{Noddy} example. To support
+ cyclic garbage collection, types need to fill two slots and set a
+ class flag that enables these slots:
+
+ \verbatiminput{noddy4.c}
+
+ The traversal method provides access to subobjects that
+ could participate in cycles:
+
+ \begin{verbatim}
+ static int
+ Noddy_traverse(Noddy *self, visitproc visit, void *arg)
+ {
+ if (self->first && visit(self->first, arg) < 0)
+ return -1;
+ if (self->last && visit(self->last, arg) < 0)
+ return -1;
+
+ return 0;
+ }
+ \end{verbatim}
+
+ For each subobject that can participate in cycles, we need to call the
+ \cfunction{visit} function passed to the traversal method passing the
+ subobject and the extra argument passed to the traversal method.
+
+ 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
+ Noddy_clear(Noddy *self)
+ {
+ Py_XDECREF(self->first);
+ self->first = NULL;
+ Py_XDECREF(self->last);
+ self->last = NULL;
+
+ return 0;
+ }
+
+ static void
+ Noddy_dealloc(Noddy* self)
+ {
+ Noddy_clear(self);
+ self->ob_type->tp_free((PyObject*)self);
+ }
+ \end{verbatim}
+
+ Finally, we add the \constant{Py_TPFLAGS_HAVE_GC} flag to the class flags:
+
+ \begin{verbatim}
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ \end{verbatim}
+
+ That's pretty much it. If we had written custom \member{tp_alloc} or
+ \member{tp_free} slots, we'd need to modify then for cyclic-garbage
+ collection. Most extensions will use the versions automatically
+ provided.
+
\section{Type Methods
\label{dnt-type-methods}}
***************
*** 1304,1331 ****
avoiding the exception can yield slightly better performance. If an
actual error occurs, it should set an exception and return \NULL.
-
-
- \subsection{Supporting the Cycle Collector
- \label{example-cycle-support}}
-
- This example shows only enough of the implementation of an extension
- type to show how the garbage collector support needs to be added. It
- shows the definition of the object structure, the
- \member{tp_traverse}, \member{tp_clear} and \member{tp_dealloc}
- implementations, the type structure, and a constructor --- the module
- initialization needed to export the constructor to Python is not shown
- as there are no special considerations there for the collector. To
- make this interesting, assume that the module exposes ways for the
- \member{container} field of the object to be modified. Note that
- since no checks are made on the type of the object used to initialize
- \member{container}, we have to assume that it may be a container.
-
- \verbatiminput{cycle-gc.c}
-
- Full details on the APIs related to the cycle detector are in
- \ulink{Supporting Cyclic Garbarge
- Collection}{../api/supporting-cycle-detection.html} in the
- \citetitle[../api/api.html]{Python/C API Reference Manual}.
-
\subsection{More Suggestions}
--- 1395,1398 ----
Index: setup.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/ext/setup.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** setup.py 28 Jun 2003 11:54:20 -0000 1.1
--- setup.py 28 Jun 2003 13:29:16 -0000 1.2
***************
*** 5,8 ****
--- 5,9 ----
Extension("noddy2", ["noddy2.c"]),
Extension("noddy3", ["noddy3.c"]),
+ Extension("noddy4", ["noddy4.c"]),
])
Index: test.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Doc/ext/test.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -C2 -d -r1.1 -r1.2
*** test.py 28 Jun 2003 11:54:40 -0000 1.1
--- test.py 28 Jun 2003 13:29:16 -0000 1.2
***************
*** 107,110 ****
--- 107,203 ----
>>> del n1
>>> del n2
+
+ Noddy 4
+
+ >>> import noddy4
+ >>> n1 = noddy4.Noddy('jim', 'fulton', 42)
+ >>> n1.first
+ 'jim'
+ >>> n1.last
+ 'fulton'
+ >>> n1.number
+ 42
+ >>> n1.name()
+ 'jim fulton'
+ >>> n1.first = 'will'
+ >>> n1.name()
+ 'will fulton'
+ >>> n1.last = 'tell'
+ >>> n1.name()
+ 'will tell'
+ >>> del n1.first
+ >>> n1.name()
+ Traceback (most recent call last):
+ ...
+ AttributeError: first
+ >>> n1.first
+ Traceback (most recent call last):
+ ...
+ AttributeError: first
+ >>> n1.first = 'drew'
+ >>> n1.first
+ 'drew'
+ >>> del n1.number
+ Traceback (most recent call last):
+ ...
+ TypeError: can't delete numeric/char attribute
+ >>> n1.number=2
+ >>> n1.number
+ 2
+ >>> n1.first = 42
+ >>> n1.name()
+ '42 tell'
+ >>> n2 = noddy4.Noddy()
+ >>> n2 = noddy4.Noddy()
+ >>> n2 = noddy4.Noddy()
+ >>> n2 = noddy4.Noddy()
+ >>> n2.name()
+ ' '
+ >>> n2.first
+ ''
+ >>> n2.last
+ ''
+ >>> del n2.first
+ >>> n2.first
+ Traceback (most recent call last):
+ ...
+ AttributeError: first
+ >>> n2.first
+ Traceback (most recent call last):
+ ...
+ AttributeError: first
+ >>> n2.name()
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ AttributeError: first
+ >>> n2.number
+ 0
+ >>> n3 = noddy4.Noddy('jim', 'fulton', 'waaa')
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ TypeError: an integer is required
+
+
+ Test cyclic gc(?)
+
+ >>> import gc
+ >>> gc.disable()
+
+ >>> x = []
+ >>> l = [x]
+ >>> n2.first = l
+ >>> n2.first
+ [[]]
+ >>> l.append(n2)
+ >>> del l
+ >>> del n1
+ >>> del n2
+ >>> sys.getrefcount(x)
+ 3
+ >>> ignore = gc.collect()
+ >>> sys.getrefcount(x)
+ 2
+
+ >>> gc.enable()
"""
--- cycle-gc.c DELETED ---