[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 ---