> attached goes a patch adding gnome_vfs_async support to gnome-python. > The actual code goes in vfs-async-handle.c, also attached [1]. Doh!, wrong file sent. Attached goes the latest and greatest vfs-async-handle.c
Regards, Iñaki
/* -*- mode: C; c-basic-offset: 4 -*- */ #include <pygobject.h> #include "pygnomevfs-private.h" #include <libgnomevfs/gnome-vfs-async-ops.h> #include <libgnomevfs/gnome-vfs-job-limit.h> typedef struct { PyObject_HEAD; GnomeVFSAsyncHandle *fd; } PyGnomeVFSAsyncHandle; extern PyTypeObject PyGnomeVFSAsyncHandle_Type; static PyObject * pygnome_vfs_async_handle_new(GnomeVFSAsyncHandle *fd) { PyGnomeVFSAsyncHandle *self; self = PyObject_NEW(PyGnomeVFSAsyncHandle, &PyGnomeVFSAsyncHandle_Type); if (self == NULL) return NULL; self->fd = fd; return (PyObject *)self; } static void async_pass (void) { } /* Get the exception in case any error has occured. Return 1 in case any error happened. */ static PyObject * fetch_exception (GnomeVFSResult result, gboolean *error_happened) { PyObject *retval; if (pygnome_vfs_result_check(result)) { retval = PyErr_Occurred(); if (error_happened) *error_happened = TRUE; } else { retval = Py_None; if (error_happened) *error_happened = FALSE; } Py_INCREF (retval); PyErr_Clear (); return retval; } static void pygvhandle_dealloc(PyGnomeVFSAsyncHandle *self) { /* XXXX: unblock threads here */ if (self->fd) { gnome_vfs_async_close(self->fd, (GnomeVFSAsyncCloseCallback)async_pass, NULL); } PyObject_FREE(self); } typedef struct { PyObject *func, *data; PyGnomeVFSAsyncHandle *self; enum { ASYNC_NOTIFY_OPEN, ASYNC_NOTIFY_READ, ASYNC_NOTIFY_WRITE, ASYNC_NOTIFY_CLOSE, ASYNC_NOTIFY_G_INFO, ASYNC_NOTIFY_LOAD_DIRECTORY, ASYNC_NOTIFY_CREATE, ASYNC_NOTIFY_CREATE_SYMLINK } origin; PyObject *extra; } PyGVFSAsyncNotify; static PyGVFSAsyncNotify * async_notify_new (PyObject *func, void *self, PyObject *data, int origin) { PyGVFSAsyncNotify *result = g_new0(PyGVFSAsyncNotify, 1); result->func = func; result->self = (PyGnomeVFSAsyncHandle *)self; result->data = data; result->origin = origin; Py_INCREF (func); Py_INCREF ((PyGnomeVFSAsyncHandle*)self); Py_XINCREF (data); return result; } static void async_notify_free (PyGVFSAsyncNotify *notify) { Py_DECREF (notify->func); Py_DECREF (notify->self); Py_XDECREF(notify->data); Py_XDECREF(notify->extra); g_free (notify); } /* Remember to decref the returned object after using it */ static GnomeVFSURI * _object_to_uri (const char *name, PyObject *uri) { if (PyObject_TypeCheck(uri, &PyGnomeVFSURI_Type)) { return gnome_vfs_uri_ref (pygnome_vfs_uri_get (uri)); } else if (PyString_Check (uri)) { GnomeVFSURI *c_uri = gnome_vfs_uri_new (PyString_AsString (uri)); if (c_uri == NULL) PyErr_SetString(PyExc_TypeError, "Cannot build a gnome.vfs.URI"); return c_uri; } else { gchar * buffer = g_strdup_printf ("'%s' must be a gnome.vfs.URI or a string", name); PyErr_SetString(PyExc_TypeError, buffer); g_free (buffer); } return NULL; } #define object_to_uri(OBJECT) _object_to_uri(#OBJECT, OBJECT) static int pygvhandle_init(PyGnomeVFSAsyncHandle *self, PyObject *args, PyObject *kwargs) { return 0; } static void callback_marshal (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, PyGVFSAsyncNotify *notify) { PyObject *retobj; PyObject *exception; gboolean error_happened; pyg_block_threads (); exception = fetch_exception (result, &error_happened); if (error_happened && (notify->origin == ASYNC_NOTIFY_OPEN || notify->origin == ASYNC_NOTIFY_CREATE)) { notify->self->fd = NULL; } if (notify->origin == ASYNC_NOTIFY_CREATE_SYMLINK) notify->self->fd = NULL; if (notify->data) retobj = PyEval_CallFunction (notify->func, "(OOO)", notify->self, exception, notify->data); else retobj = PyObject_CallFunction (notify->func, "(OO)", notify->self, exception); if (retobj == NULL) { PyErr_Print(); PyErr_Clear(); } Py_XDECREF (retobj); Py_DECREF (exception); async_notify_free (notify); pyg_unblock_threads (); } static PyObject* pygvfs_async_open(PyObject *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "uri", "callback", "open_mode", "priority", "data", NULL }; PyObject *uri; GnomeVFSOpenMode open_mode = GNOME_VFS_OPEN_READ; PyObject *callback; PyObject *data = NULL; int priority = GNOME_VFS_PRIORITY_DEFAULT; PyObject *pyself; GnomeVFSURI *c_uri; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iiO:gnome.vfs.async.open", kwlist, &uri, &callback, &open_mode, &priority, &data)) return NULL; /* XXXX: unblock threads here */ if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "'callback' argument not callable"); return NULL; } c_uri = object_to_uri (uri); if (c_uri == NULL) return NULL; pyself = pygnome_vfs_async_handle_new (NULL); gnome_vfs_async_open_uri(&((PyGnomeVFSAsyncHandle*)pyself)->fd, c_uri, open_mode, priority, (GnomeVFSAsyncOpenCallback)callback_marshal, async_notify_new (callback, pyself, data, ASYNC_NOTIFY_OPEN)); gnome_vfs_uri_unref (c_uri); return pyself; } static PyObject* pygvhandle_close(PyGnomeVFSAsyncHandle *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "callback", "data", NULL }; PyObject *callback; PyObject *data = NULL; if (!self->fd) { PyErr_SetString(PyExc_ValueError, "I/O operation on closed handle"); return NULL; } if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O:gnome.vfs.async.Handle.close", kwlist, &callback, &data)) return NULL; if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "'callback' argument not callable"); return NULL; } gnome_vfs_async_close(self->fd, (GnomeVFSAsyncCloseCallback)callback_marshal, async_notify_new (callback, self, data, ASYNC_NOTIFY_CLOSE)); self->fd = NULL; Py_INCREF (Py_None); return Py_None; } static void read_write_marshal (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer buffer, GnomeVFSFileSize requested, GnomeVFSFileSize done, PyGVFSAsyncNotify *notify) { PyObject *retobj; PyObject *pyvalue; PyObject *exception; gboolean error_happened; pyg_block_threads (); exception = fetch_exception (result, &error_happened); if (notify->origin == ASYNC_NOTIFY_READ) pyvalue = PyString_FromStringAndSize (buffer, done); else pyvalue = PyInt_FromLong (done); if (notify->data) retobj = PyEval_CallFunction (notify->func, "(OOOO)", notify->self, pyvalue, exception, notify->data); else retobj = PyObject_CallFunction (notify->func, "(OOO)", notify->self, pyvalue, exception); if (retobj == NULL) { PyErr_Print(); PyErr_Clear(); } Py_XDECREF (retobj); Py_DECREF (pyvalue); Py_DECREF (exception); if (notify->origin == ASYNC_NOTIFY_READ) g_free (buffer); async_notify_free (notify); pyg_unblock_threads (); } static PyObject* pygvhandle_read (PyGnomeVFSAsyncHandle *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "bytes", "callback", "data", NULL }; glong bytes; PyObject *data = NULL; PyObject *callback; if (!self->fd) { PyErr_SetString(PyExc_ValueError, "I/O operation on closed handle"); return NULL; } if (!PyArg_ParseTupleAndKeywords(args, kwargs, "lO|O:gnome.vfs.async.Handle.read", kwlist, &bytes, &callback, &data)) return NULL; if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "third argument not callable"); return NULL; } gnome_vfs_async_read (self->fd, g_malloc(bytes), bytes, (GnomeVFSAsyncReadCallback)read_write_marshal, async_notify_new (callback, self, data, ASYNC_NOTIFY_READ)); Py_INCREF (Py_None); return Py_None; } static PyObject* pygvhandle_write (PyGnomeVFSAsyncHandle *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "buffer", "callback", "data", NULL }; PyObject *buffer; PyObject *data = NULL; PyObject *callback; PyGVFSAsyncNotify *notify; if (!self->fd) { PyErr_SetString(PyExc_ValueError, "I/O operation on closed handle"); return NULL; } if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|O:gnome.vfs.async.Handle.write", kwlist, &buffer, &callback, &data)) return NULL; if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "'callback' argument not callable"); return NULL; } if (!PyString_Check(buffer)) { PyErr_SetString(PyExc_TypeError, "'buffer' must be a string object"); return NULL; } Py_INCREF (buffer); notify = async_notify_new (callback, self, data, ASYNC_NOTIFY_WRITE); notify->extra = buffer; gnome_vfs_async_write (self->fd, PyString_AsString(buffer), PyString_Size (buffer), (GnomeVFSAsyncWriteCallback)read_write_marshal, notify); Py_INCREF (Py_None); return Py_None; } static void get_info_marshal (GnomeVFSAsyncHandle *handle, GList *results, PyGVFSAsyncNotify *notify) { PyObject *retobj; PyObject *pyresults; /* a list of (uri, exception, info) tuples */ gint length; gint i; pyg_block_threads (); notify->self->fd = NULL; length = g_list_length (results); pyresults = PyList_New (length); for (i=0; i<length; i++, results = results->next) { PyObject *item = PyTuple_New(3); GnomeVFSGetFileInfoResult *r = results->data; gnome_vfs_uri_ref (r->uri); PyTuple_SetItem (item, 0, pygnome_vfs_uri_new (r->uri)); PyTuple_SetItem (item, 1, fetch_exception (r->result, NULL)); gnome_vfs_file_info_ref (r->file_info); PyTuple_SetItem (item, 2, pygnome_vfs_file_info_new (r->file_info)); PyList_SetItem (pyresults, i, item); } if (notify->data) retobj = PyEval_CallFunction (notify->func, "(OOO)", notify->self, pyresults, notify->data); else retobj = PyObject_CallFunction (notify->func, "(OO)", notify->self, pyresults); if (retobj == NULL) { PyErr_Print(); PyErr_Clear(); } Py_XDECREF (retobj); Py_DECREF (pyresults); async_notify_free (notify); pyg_unblock_threads (); } static PyObject * pygvfs_async_get_file_info (PyObject *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "urilist", "callback", "options", "priority", "data", NULL }; PyObject *py_urilist; GList *urilist = NULL; GnomeVFSFileInfoOptions options = GNOME_VFS_FILE_INFO_DEFAULT; PyObject *callback; PyObject *data = NULL; int priority = GNOME_VFS_PRIORITY_DEFAULT; int size, i; PyObject *pyself; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iiO:gnome.vfs.async.get_file_info", kwlist, &py_urilist, &callback, &options, &priority, &data)) return NULL; /* XXXX: unblock threads here */ if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "'callback' argument not callable"); return NULL; } if (PyString_Check(py_urilist)) { urilist = g_list_append (urilist, gnome_vfs_uri_new(PyString_AsString (py_urilist))); } else if (PyObject_TypeCheck(py_urilist, &PyGnomeVFSURI_Type)) { urilist = g_list_append (urilist, gnome_vfs_uri_ref (pygnome_vfs_uri_get (py_urilist))); } else if (PySequence_Check(py_urilist)) { size = PySequence_Size(py_urilist); for (i = 0; i < size; ++i) { PyObject *item = PySequence_GetItem(py_urilist, i); GnomeVFSURI *uri = NULL; if (PyObject_TypeCheck(item, &PyGnomeVFSURI_Type)) uri = gnome_vfs_uri_ref(pygnome_vfs_uri_get(item)); else if (PyString_Check(item)) { uri = gnome_vfs_uri_new(PyString_AsString(item)); } else { PyErr_SetString(PyExc_TypeError, "all items in sequence must be of string type or gnome.vfs.URI"); return NULL; } urilist = g_list_append(urilist, uri); Py_DECREF (item); } } else { PyErr_SetString(PyExc_TypeError, "'urilist' must be either a string, gnome.vfs.URI or a sequence of those"); return NULL; } pyself = pygnome_vfs_async_handle_new (NULL); gnome_vfs_async_get_file_info(&((PyGnomeVFSAsyncHandle*)pyself)->fd, urilist, options, priority, (GnomeVFSAsyncGetFileInfoCallback)get_info_marshal, async_notify_new (callback, pyself, data, ASYNC_NOTIFY_G_INFO)); while (urilist) { gnome_vfs_uri_unref ((GnomeVFSURI*)urilist->data); urilist = urilist->next; } g_list_free (urilist); return pyself; } static PyObject * pygvhandle_is_open (PyGnomeVFSAsyncHandle *self) { return PyInt_FromLong (self->fd != NULL); } static PyObject * pygvhandle_cancel (PyGnomeVFSAsyncHandle *self) { if (self->fd) { gnome_vfs_async_cancel (self->fd); self->fd = NULL; } Py_INCREF (Py_None); return Py_None; } static void load_dir_marshal (GnomeVFSAsyncHandle *handle, GnomeVFSResult result, GList *list, guint length, PyGVFSAsyncNotify *notify) { PyObject *retobj; PyObject *pyresults; /* a list of gnome.vfs.FileInfo */ gint i; gboolean error_happened; PyObject *exception; pyg_block_threads (); exception = fetch_exception (result, &error_happened); if (error_happened && notify->origin == ASYNC_NOTIFY_LOAD_DIRECTORY) notify->self->fd = NULL; pyresults = PyList_New (length); for (i=0; i<length; i++, list = list->next) { GnomeVFSFileInfo *info = list->data; gnome_vfs_file_info_ref (info); PyList_SetItem (pyresults, i, pygnome_vfs_file_info_new (info)); } if (notify->data) retobj = PyEval_CallFunction (notify->func, "(OOOO)", notify->self, pyresults, exception, notify->data); else retobj = PyObject_CallFunction (notify->func, "(OOO)", notify->self, pyresults, exception); if (retobj == NULL) { PyErr_Print(); PyErr_Clear(); } Py_XDECREF (retobj); Py_DECREF (pyresults); Py_DECREF (exception); /* Supposedly we don't get called after errors? */ if (error_happened) async_notify_free (notify); pyg_unblock_threads (); } static PyObject* pygvfs_async_load_directory (PyObject *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "uri", "callback", "options", "items_per_notification", "priority", "data", NULL }; PyObject *uri; PyObject *callback; GnomeVFSFileInfoOptions options = GNOME_VFS_FILE_INFO_DEFAULT; guint items_per_notification = 20; /* Some default to keep the order of the parameters as in the C API */ int priority = GNOME_VFS_PRIORITY_DEFAULT; PyObject *data = NULL; PyObject *pyself; GnomeVFSURI *c_uri; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iIiO:gnome.vfs.async.load_directory", kwlist, &uri, &callback, &options, &items_per_notification, &priority, &data)) return NULL; /* XXXX: unblock threads here */ if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "'callback' argument not callable"); return NULL; } c_uri = object_to_uri (uri); if (c_uri == NULL) return NULL; pyself = pygnome_vfs_async_handle_new (NULL); gnome_vfs_async_load_directory_uri(&((PyGnomeVFSAsyncHandle*)pyself)->fd, c_uri, options, items_per_notification, priority, (GnomeVFSAsyncDirectoryLoadCallback)load_dir_marshal, async_notify_new (callback, pyself, data, ASYNC_NOTIFY_LOAD_DIRECTORY)); gnome_vfs_uri_unref (c_uri); return pyself; } static PyObject* pygvfs_async_create (PyObject *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "uri", "callback", "open_mode", "exclusive", "perm", "priority", "data", NULL }; PyObject *uri; PyObject *callback; GnomeVFSOpenMode open_mode = GNOME_VFS_OPEN_READ | GNOME_VFS_OPEN_WRITE; gboolean exclusive = FALSE; guint perm = 0666; int priority = GNOME_VFS_PRIORITY_DEFAULT; PyObject *data = NULL; PyObject *pyself; GnomeVFSURI *c_uri; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|iiiiO:gnome.vfs.async.create", kwlist, &uri, &callback, &open_mode, &exclusive, &perm, &priority, &data)) return NULL; /* XXXX: unblock threads here */ if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "'callback' argument not callable"); return NULL; } c_uri = object_to_uri (uri); if (c_uri == NULL) return NULL; pyself = pygnome_vfs_async_handle_new (NULL); gnome_vfs_async_create_uri(&((PyGnomeVFSAsyncHandle*)pyself)->fd, c_uri, open_mode, exclusive, perm, priority, (GnomeVFSAsyncOpenCallback)callback_marshal, async_notify_new (callback, pyself, data, ASYNC_NOTIFY_CREATE)); gnome_vfs_uri_unref (c_uri); return pyself; } static PyObject* pygvfs_async_create_symbolic_link (PyObject *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "uri", "reference", "callback", "priority", "data", NULL }; PyObject *uri; PyObject *reference; PyObject *callback; int priority = GNOME_VFS_PRIORITY_DEFAULT; PyObject *data = NULL; PyObject *pyself; GnomeVFSURI *c_uri, *c_reference; gchar *reference_buffer; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOO|iO:gnome.vfs.async.create_symbolic_link", kwlist, &uri, &reference, &callback, &priority, &data)) return NULL; /* XXXX: unblock threads here */ if (!PyCallable_Check(callback)) { PyErr_SetString(PyExc_TypeError, "'callback' argument not callable"); return NULL; } c_uri = object_to_uri (uri); if (c_uri == NULL) return NULL; c_reference = object_to_uri (reference); if (c_reference == NULL) { gnome_vfs_uri_unref (c_uri); return NULL; } /* hmmm... */ reference_buffer = gnome_vfs_uri_to_string (c_reference, GNOME_VFS_URI_HIDE_NONE); pyself = pygnome_vfs_async_handle_new (NULL); gnome_vfs_async_create_symbolic_link(&((PyGnomeVFSAsyncHandle*)pyself)->fd, c_uri, reference_buffer, priority, (GnomeVFSAsyncOpenCallback)callback_marshal, async_notify_new (callback, pyself, data, ASYNC_NOTIFY_CREATE_SYMLINK)); g_free (reference_buffer); gnome_vfs_uri_unref (c_uri); gnome_vfs_uri_unref (c_reference); return pyself; } static PyObject * pygvfs_async_get_job_limit (PyObject *self) { return PyInt_FromLong (gnome_vfs_async_get_job_limit ()); } static PyObject* pygvfs_async_set_job_limit (PyObject *self, PyObject *args, PyObject *kwargs) { static char *kwlist[] = { "limit", NULL }; int limit; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i:gnome.vfs.async.set_job_limit", kwlist, &limit)) return NULL; gnome_vfs_async_set_job_limit (limit); Py_INCREF (Py_None); return Py_None; } static PyMethodDef pygnomevfs_async_functions[] = { { "open", (PyCFunction)pygvfs_async_open, METH_VARARGS|METH_KEYWORDS }, { "get_file_info", (PyCFunction)pygvfs_async_get_file_info, METH_VARARGS|METH_KEYWORDS }, { "load_directory", (PyCFunction)pygvfs_async_load_directory, METH_VARARGS|METH_KEYWORDS }, { "create", (PyCFunction)pygvfs_async_create, METH_VARARGS|METH_KEYWORDS }, { "create_symbolic_link", (PyCFunction)pygvfs_async_create_symbolic_link, METH_VARARGS|METH_KEYWORDS }, { "get_job_limit", (PyCFunction)pygvfs_async_get_job_limit, METH_NOARGS }, { "set_job_limit", (PyCFunction)pygvfs_async_set_job_limit, METH_VARARGS|METH_KEYWORDS }, { NULL, NULL, 0} }; PyObject * pygvfs_async_module_init (void) { PyObject *m; PyObject *d; PyGnomeVFSAsyncHandle_Type.ob_type = &PyType_Type; if (PyType_Ready(&PyGnomeVFSAsyncHandle_Type) < 0) return NULL; m = Py_InitModule("gnome.vfs.async", pygnomevfs_async_functions); d = PyModule_GetDict(m); PyDict_SetItemString(d, "Handle", (PyObject *)&PyGnomeVFSAsyncHandle_Type); return m; } static PyMethodDef pygvhandle_methods[] = { { "close", (PyCFunction)pygvhandle_close, METH_VARARGS|METH_KEYWORDS }, { "read", (PyCFunction)pygvhandle_read, METH_VARARGS|METH_KEYWORDS }, { "write", (PyCFunction)pygvhandle_write, METH_VARARGS|METH_KEYWORDS }, { "is_open", (PyCFunction)pygvhandle_is_open, METH_NOARGS }, { "cancel", (PyCFunction)pygvhandle_cancel, METH_NOARGS }, { NULL, NULL, 0 } }; PyTypeObject PyGnomeVFSAsyncHandle_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "gnome.vfs.async.Handle", /* tp_name */ sizeof(PyGnomeVFSAsyncHandle), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)pygvhandle_dealloc, /* tp_dealloc */ (printfunc)0, /* tp_print */ (getattrfunc)0, /* tp_getattr */ (setattrfunc)0, /* tp_setattr */ (cmpfunc)0, /* tp_compare */ (reprfunc)0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ (hashfunc)0, /* tp_hash */ (ternaryfunc)0, /* tp_call */ (reprfunc)0, /* tp_str */ (getattrofunc)0, /* tp_getattro */ (setattrofunc)0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ NULL, /* Documentation string */ (traverseproc)0, /* tp_traverse */ (inquiry)0, /* tp_clear */ (richcmpfunc)0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)0, /* tp_iter */ (iternextfunc)0, /* tp_iternext */ pygvhandle_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ (PyTypeObject *)0, /* tp_base */ (PyObject *)0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)pygvhandle_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ PyType_GenericNew, /* tp_new */ 0, /* tp_free */ (inquiry)0, /* tp_is_gc */ (PyObject *)0, /* tp_bases */ };
_______________________________________________ pygtk mailing list [EMAIL PROTECTED] http://www.daa.com.au/mailman/listinfo/pygtk Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/