[pygobject/gsoc2009: 130/160] Get rid of GIStruct



commit 28f26269be4df370eb229a03b43115945cf24972
Author: Simon van der Linden <svdlinden src gnome org>
Date:   Tue Aug 11 17:15:23 2009 +0200

    Get rid of GIStruct
    
    Make structures inherit from GBoxed. Add a new constructor to GBoxed, which is
    able to create a structure instance when it is simple (without pointers).

 gi/Makefile.am             |    2 +
 gi/gimodule.c              |   41 ++++++++-----------
 gi/module.py               |   46 +++++++++++-----------
 gi/pygi-argument.c         |   20 +--------
 gi/pygi-info.c             |   21 +--------
 gi/pygi-private.h          |    5 +-
 gi/pygi.h                  |   15 +++++++-
 gi/types.py                |   60 +++++++++++++---------------
 gobject/pygboxed.c         |   37 ++++++++++++++++-
 tests/test_girepository.py |   93 +++++++++++++++-----------------------------
 10 files changed, 158 insertions(+), 182 deletions(-)
---
diff --git a/gi/Makefile.am b/gi/Makefile.am
index e0404a7..c7505ce 100644
--- a/gi/Makefile.am
+++ b/gi/Makefile.am
@@ -42,6 +42,8 @@ _gi_la_SOURCES = \
 	pygi-info.h \
 	pygi-argument.c \
 	pygi-argument.h \
+	pygi-boxed.c \
+	pygi-boxed.h \
 	pygi.h \
 	pygi-private.h \
 	gimodule.c
diff --git a/gi/gimodule.c b/gi/gimodule.c
index bccc62a..d9a3372 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -75,35 +75,29 @@ pygi_type_find_by_gi_info (GIBaseInfo *info)
     return pygi_type_find_by_name(namespace_, name);
 }
 
-gpointer
-_pygi_object_get_buffer (PyObject *object,
-                         gsize    *size)
+GIBaseInfo *
+_pygi_object_get_gi_info (PyObject     *object,
+                          PyTypeObject *type)
 {
-    PyBufferProcs *py_buffer_procs;
-    PyObject *py_buffer;
-    gpointer buffer;
+    PyObject *py_info;
+    GIBaseInfo *info = NULL;
 
-    py_buffer = PyObject_GetAttrString(object, "__buffer__");
-    if (py_buffer == NULL) {
-        return NULL;
+    py_info = PyObject_GetAttrString(object, "__info__");
+    if (py_info == NULL) {
+        goto out;
     }
-
-    if(!PyBuffer_Check(py_buffer)) {
-        PyErr_Format(PyExc_TypeError, "Must be buffer, not %s",
-                object->ob_type->tp_name);
+    if (!PyObject_TypeCheck(py_info, type)) {
+        PyErr_Format(PyExc_TypeError, "attribute '__info__' must be %s, not %s",
+            type->tp_name, py_info->ob_type->tp_name);
+        goto out;
     }
 
-    /* We don't need to keep a reference. */
-    Py_DECREF(py_buffer);
-
-    py_buffer_procs = py_buffer->ob_type->tp_as_buffer;
+    info = PyGIBaseInfo_GET_GI_INFO(py_info);
 
-    *size = (*py_buffer_procs->bf_getreadbuffer)(py_buffer, 0, &buffer);
-    if (*size < 0) {
-        return NULL;
-    }
+out:
+    Py_XDECREF(py_info);
 
-    return buffer;
+    return info;
 }
 
 static PyObject *
@@ -129,7 +123,8 @@ static PyMethodDef _pygi_functions[] = {
 };
 
 struct PyGI_API PyGI_API = {
-    pygi_type_find_by_gi_info
+    pygi_type_find_by_gi_info,
+    pygi_boxed_new
 };
 
 PyMODINIT_FUNC
diff --git a/gi/module.py b/gi/module.py
index 7311dbd..a06db4e 100644
--- a/gi/module.py
+++ b/gi/module.py
@@ -26,7 +26,7 @@ import gobject
 from gobject import \
     GObject, \
     GInterface, \
-    GEnum
+    GBoxed
 
 from ._gi import \
     Repository, \
@@ -38,8 +38,8 @@ from ._gi import \
     InterfaceInfo, \
     StructInfo
 from .types import \
-    GObjectIntrospectionMeta, \
-    GIStruct, \
+    GObjectMeta, \
+    GBoxedMeta, \
     Function
 
 repository = Repository.get_default()
@@ -74,16 +74,7 @@ class DynamicModule(object):
             raise AttributeError("%r object has no attribute %r" % (
                     self.__class__.__name__, name))
 
-        if isinstance(info, StructInfo):
-            # FIXME: This could be wrong for structures that are registered (like GValue or GClosure).
-            bases = (GIStruct,)
-            name = info.get_name()
-            dict_ = {
-                '__info__': info,
-                '__module__': info.get_namespace()
-            }
-            value = GObjectIntrospectionMeta(name, bases, dict_)
-        elif isinstance(info, EnumInfo):
+        if isinstance(info, EnumInfo):
             type_ = info.get_g_type()
             if type_.is_a(gobject.TYPE_ENUM):
                 value = gobject.enum_from_g_type(type_)
@@ -99,29 +90,38 @@ class DynamicModule(object):
                 setattr(value, name, value(value_info.get_value()))
 
         elif isinstance(info, RegisteredTypeInfo):
+            g_type = info.get_g_type()
+
             # Check if there is already a Python wrapper.
-            gtype = info.get_g_type()
-            if gtype.pytype is not None:
-                self.__dict__[name] = gtype.pytype
-                return
+            if g_type != gobject.TYPE_NONE and g_type.pytype is not None:
+                self.__dict__[name] = g_type.pytype
+                return gtype.pytype
 
             # Create a wrapper.
             if isinstance(info, ObjectInfo):
-                parent = get_parent_for_object(info)
-                bases = (parent,)
+                bases = (get_parent_for_object(info),)
+                metaclass = GObjectMeta
             elif isinstance(info, InterfaceInfo):
+                # FIXME
                 bases = (GInterface,)
+                metaclass = GObjectMeta
+            elif isinstance(info, StructInfo):
+                bases = (GBoxed,)
+                metaclass = GBoxedMeta
             else:
                 raise NotImplementedError(info)
 
             name = info.get_name()
             dict_ = {
                 '__info__': info,
-                '__module__': info.get_namespace(),
-                '__gtype__': gtype
+                '__module__': self.__namespace__,
+                '__gtype__': g_type
             }
-            value = GObjectIntrospectionMeta(name, bases, dict_)
-            gtype.pytype = value
+            value = metaclass(name, bases, dict_)
+
+            if g_type != gobject.TYPE_NONE:
+                g_type.pytype = value
+
         elif isinstance(info, FunctionInfo):
             value = Function(info)
         else:
diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index 868b4ae..e4f12a8 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -906,8 +906,6 @@ array_item_error:
                 case GI_INFO_TYPE_STRUCT:
                 {
                     GType type;
-                    gsize size;
-                    gpointer buffer;
 
                     type = g_registered_type_info_get_g_type((GIRegisteredTypeInfo *)info);
 
@@ -953,9 +951,7 @@ array_item_error:
                         break;
                     }
 
-                    buffer = _pygi_object_get_buffer(object, &size);
-
-                    arg.v_pointer = buffer;
+                    arg.v_pointer = pyg_boxed_get(object, void);
                     break;
                 }
                 case GI_INFO_TYPE_OBJECT:
@@ -1332,8 +1328,6 @@ _pygi_argument_to_object (GArgument  *arg,
                 {
                     GType type;
                     PyObject *py_type = NULL;
-                    gsize size;
-                    PyObject *buffer = NULL;
 
                     /* Handle special cases first. */
                     type = g_registered_type_info_get_g_type((GIRegisteredTypeInfo *)info);
@@ -1343,23 +1337,13 @@ _pygi_argument_to_object (GArgument  *arg,
                         break;
                     }
 
-                    /* Create a Python buffer. */
-                    size = g_struct_info_get_size((GIStructInfo *)info);
-                    buffer = PyBuffer_FromReadWriteMemory(arg->v_pointer, size);
-                    if (buffer == NULL) {
-                        break;
-                    }
-
-                    /* Wrap the structure. */
                     py_type = pygi_type_find_by_gi_info(info);
                     if (py_type == NULL) {
-                        Py_DECREF(buffer);
                         break;
                     }
 
-                    object = PyObject_CallFunction(py_type, "O", buffer);
+                    object = pygi_boxed_new((PyTypeObject *)py_type, arg->v_pointer, TRUE /* FIXME */);
 
-                    Py_DECREF(buffer);
                     Py_DECREF(py_type);
 
                     break;
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index eb14e42..a165376 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -583,11 +583,8 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self,
                     g_assert_not_reached();
                     break;
                 case GI_INFO_TYPE_STRUCT:
-                {
-                    gsize size;
-                    in_args[0].v_pointer = _pygi_object_get_buffer(py_arg, &size);
+                    in_args[0].v_pointer = pyg_boxed_get(py_arg, void);
                     break;
-                }
                 case GI_INFO_TYPE_OBJECT:
                     in_args[0].v_pointer = pygobject_get(py_arg);
                     break;
@@ -1053,19 +1050,9 @@ _wrap_g_struct_info_get_methods (PyGIBaseInfo *self)
     return infos;
 }
 
-static PyObject *
-_wrap_g_struct_info_new_buffer (PyGIBaseInfo *self)
-{
-    gsize size = g_struct_info_get_size ((GIStructInfo*)self->info);
-    PyObject *buffer = PyBuffer_New (size);
-    Py_INCREF(buffer);
-    return buffer;
-}
-
 static PyMethodDef _PyGIStructInfo_methods[] = {
     { "get_fields", (PyCFunction)_wrap_g_struct_info_get_fields, METH_NOARGS },
     { "get_methods", (PyCFunction)_wrap_g_struct_info_get_methods, METH_NOARGS },
-    { "new_buffer", (PyCFunction)_wrap_g_struct_info_new_buffer, METH_NOARGS },
     { NULL, NULL, 0 }
 };
 
@@ -1405,8 +1392,7 @@ _wrap_g_field_info_get_value (PyGIBaseInfo *self,
 
     if (container_info_type == GI_INFO_TYPE_STRUCT
             || container_info_type == GI_INFO_TYPE_BOXED) {
-        gsize size;
-        buffer = _pygi_object_get_buffer(object, &size);
+        buffer = pyg_boxed_get(object, void);
         if (buffer == NULL) {
             goto return_;
         }
@@ -1505,8 +1491,7 @@ _wrap_g_field_info_set_value (PyGIBaseInfo *self,
 
     if (container_info_type == GI_INFO_TYPE_STRUCT
             || container_info_type == GI_INFO_TYPE_BOXED) {
-        gsize size;
-        buffer = _pygi_object_get_buffer(object, &size);
+        buffer = pyg_boxed_get(object, void);
         if (buffer == NULL) {
             goto return_;
         }
diff --git a/gi/pygi-private.h b/gi/pygi-private.h
index eabaef0..a76cffc 100644
--- a/gi/pygi-private.h
+++ b/gi/pygi-private.h
@@ -19,6 +19,7 @@
 #include "pygi-repository.h"
 #include "pygi-info.h"
 #include "pygi-argument.h"
+#include "pygi-boxed.h"
 
 G_BEGIN_DECLS
 
@@ -54,8 +55,8 @@ PyObject* pygi_type_find_by_gi_info (GIBaseInfo *info);
 
 /* Private */
 
-gpointer _pygi_object_get_buffer (PyObject *object,
-                                  gsize    *size);
+GIBaseInfo* _pygi_object_get_gi_info (PyObject     *object,
+                                      PyTypeObject *type);
 
 
 #define _PyGI_ERROR_PREFIX(format, ...) G_STMT_START { \
diff --git a/gi/pygi.h b/gi/pygi.h
index 8cf4a4e..0187ebc 100644
--- a/gi/pygi.h
+++ b/gi/pygi.h
@@ -39,18 +39,31 @@ typedef struct {
     PyObject *inst_weakreflist;
 } PyGIBaseInfo;
 
+#define PyGIBaseInfo_GET_GI_INFO(object) g_base_info_ref(((PyGIBaseInfo *)object)->info)
+
 
 struct PyGI_API {
+    /* Misc */
     PyObject* (*type_find_by_gi_info) (GIBaseInfo *info);
+
+    /* Boxed */
+    PyObject* (*boxed_new) (PyTypeObject *type,
+                            gpointer      pointer,
+                            gboolean      own_pointer);
 };
 
 
 #ifndef __PYGI_PRIVATE_H__
 
-struct PyGI_API *PyGI_API = NULL;
+static struct PyGI_API *PyGI_API = NULL;
 
+/* Misc */
 #define pygi_type_find_by_gi_info (PyGI_API->type_find_by_gi_info)
 
+/* Boxed */
+#define pygi_boxed_new (PyGI_API->boxed_new)
+
+
 static int
 pygi_import (void)
 {
diff --git a/gi/types.py b/gi/types.py
index 0dbadb2..7dfda04 100644
--- a/gi/types.py
+++ b/gi/types.py
@@ -54,26 +54,7 @@ class Field(object):
         return self.info.set_value(instance, value)
 
 
-class GObjectIntrospectionMeta(gobject.GObjectMeta):
-
-    def __init__(cls, name, bases, dict_):
-        super(GObjectIntrospectionMeta, cls).__init__(name, bases, dict_)
-
-        if hasattr(cls, '__gtype__'):
-            setObjectHasNewConstructor(cls.__gtype__)
-
-        # Only set up the wrapper methods and fields in their base classes.
-        if cls.__name__ == cls.__info__.get_name():
-            if isinstance(cls.__info__, InterfaceInfo):
-                cls._setup_methods()
-
-            if isinstance(cls.__info__, ObjectInfo):
-                cls._setup_fields()
-                cls._setup_methods()
-
-            if isinstance(cls.__info__, StructInfo):
-                cls._setup_fields()
-                cls._setup_methods()
+class MetaClassHelper(object):
 
     def _setup_methods(cls):
         constructor_infos = []
@@ -116,20 +97,33 @@ class GObjectIntrospectionMeta(gobject.GObjectMeta):
             setattr(cls, name, Field(field_info))
 
 
-class GIStruct(object):
+class GObjectMeta(gobject.GObjectMeta, MetaClassHelper):
+
+    def __init__(cls, name, bases, dict_):
+        super(GObjectMeta, cls).__init__(name, bases, dict_)
 
-    def __init__(self, buffer=None):
-        if buffer is None:
-            buffer = self.__info__.new_buffer()
-        self.__buffer__ = buffer
+        # Avoid touching anything else than the base class.
+        if cls.__name__ != cls.__info__.get_name():
+            return;
+
+        if hasattr(cls, '__gtype__'):
+            setObjectHasNewConstructor(cls.__gtype__)
+
+        cls._setup_methods()
+
+        if isinstance(cls.__info__, ObjectInfo):
+            cls._setup_fields()
+
+
+class GBoxedMeta(type, MetaClassHelper):
+
+    def __init__(cls, name, bases, dict_):
+        super(GBoxedMeta, cls).__init__(name, bases, dict_)
 
-    def __eq__(self, other):
-        for field_info in self.__info__.get_fields():
-            name = field_info.get_name()
-            if getattr(self, name) != getattr(other, name):
-                return False
-        return True
+        # Avoid touching anything else than the base class.
+        if cls.__name__ != cls.__info__.get_name():
+            return;
 
-    def __ne__(self, other):
-        return not self.__eq__(other)
+        cls._setup_fields()
+        cls._setup_methods()
 
diff --git a/gobject/pygboxed.c b/gobject/pygboxed.c
index 5d08a51..2b8e6de 100644
--- a/gobject/pygboxed.c
+++ b/gobject/pygboxed.c
@@ -25,6 +25,9 @@
 #endif
 
 #include <pyglib.h>
+#if HAVE_PYGI_H
+#    include <pygi.h>
+#endif
 #include "pygobject-private.h"
 #include "pygboxed.h"
 
@@ -38,7 +41,12 @@ pyg_boxed_dealloc(PyGBoxed *self)
 {
     if (self->free_on_dealloc && self->boxed) {
 	PyGILState_STATE state = pyglib_gil_state_ensure();
-	g_boxed_free(self->gtype, self->boxed);
+        if (!g_type_is_a(self->gtype, G_TYPE_BOXED)) {
+            /* Void pointers are also allowed. */
+            g_free(self->boxed);
+        } else {
+            g_boxed_free(self->gtype, self->boxed);
+        }
 	pyglib_gil_state_release(state);
     }
 
@@ -181,6 +189,26 @@ pyg_boxed_new(GType boxed_type, gpointer boxed, gboolean copy_boxed,
     return (PyObject *)self;
 }
 
+#if HAVE_PYGI_H
+static PyObject *
+pyg_boxed_new_ (PyTypeObject *type,
+                PyObject     *args,
+                PyObject     *kwds)
+{
+    static char *kwlist[] = { NULL };
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kwlist)) {
+        return NULL;
+    }
+
+    if (pygi_import() < 0) {
+        return NULL;
+    }
+
+    return pygi_boxed_new(type, NULL, TRUE);
+}
+#endif /* HAVE_PYGI_H */
+
 void
 pygobject_boxed_register_types(PyObject *d)
 {
@@ -194,11 +222,16 @@ pygobject_boxed_register_types(PyObject *d)
     PyGBoxed_Type.tp_methods = pygboxed_methods;
     PyGBoxed_Type.tp_free = (freefunc)pyg_boxed_free;
     PyGBoxed_Type.tp_hash = (hashfunc)pyg_boxed_hash;
-    
+#if HAVE_PYGI_H
+    PyGBoxed_Type.tp_new = (newfunc)pyg_boxed_new_;
+#endif
+
     PYGOBJECT_REGISTER_GTYPE(d, PyGBoxed_Type, "GBoxed", G_TYPE_BOXED);
 
+#if !HAVE_PYGI_H
     /* We don't want instances to be created in Python, but
      * PYGOBJECT_REGISTER_GTYPE assigned PyObject_GenericNew as instance
      * constructor. It's not too late to revert it to NULL, though. */
     PyGBoxed_Type.tp_new = (newfunc)NULL;
+#endif
 }
diff --git a/tests/test_girepository.py b/tests/test_girepository.py
index 8174881..3b59f0c 100644
--- a/tests/test_girepository.py
+++ b/tests/test_girepository.py
@@ -30,13 +30,6 @@ utf8_nonconst = 'nonconst \xe2\x99\xa5 utf8'
 test_sequence = ('1', '2', '3')
 test_dict = {'foo': 'bar', 'baz': 'bat', 'qux': 'quux'}
 
-def createStructA():
-    a = Everything.TestStructA()
-    a.some_int = 3
-    a.some_int8 = 1
-    a.some_double = 4.15
-    a.some_enum= Everything.TestEnum.VALUE3
-    return a
 
 class SignalHandler:
     def __init__(self):
@@ -545,7 +538,19 @@ class TestGIEverything(unittest.TestCase):
 # Structures
 
     def testStructA(self):
-        a = createStructA()
+        # Test inheritance.
+        self.assertTrue(issubclass(Everything.TestStructA, gobject.GBoxed))
+
+        # Test instanciation.
+        a = Everything.TestStructA()
+        self.assertTrue(isinstance(a, Everything.TestStructA))
+
+        # Test fields.
+        a.some_int = 3
+        a.some_int8 = 1
+        a.some_double = 4.15
+        a.some_enum= Everything.TestEnum.VALUE3
+
         self.assertEquals(a.some_int, 3)
         self.assertEquals(a.some_int8, 1)
         self.assertEquals(a.some_double, 4.15)
@@ -555,74 +560,38 @@ class TestGIEverything(unittest.TestCase):
         self.assertRaises(ValueError, setattr, a, 'some_int8', INT8_MIN-1)
         self.assertRaises(ValueError, setattr, a, 'some_int8', INT8_MAX+1)
 
+        # Test method call and marshalling.
         a_out = Everything.TestStructA()
         a.clone(a_out)
-        self.assertEquals(a, a_out)
+
+        self.assertEquals(a.some_int, a_out.some_int)
+        self.assertEquals(a.some_int8, a_out.some_int8)
+        self.assertEquals(a.some_double, a_out.some_double)
+        self.assertEquals(a.some_enum, a_out.some_enum)
 
         # Test instance checking by passing a wrong instance.
         self.assertRaises(TypeError, Everything.TestStructA.clone, 'a', a_out)
 
     def testStructB(self):
         b = Everything.TestStructB()
-        b.some_int8 = 3
-        a = createStructA()
+        a = Everything.TestStructA()
+
+        # Test nested structures.
         b.nested_a = a
-        self.assertEquals(a, b.nested_a)
+        a_out = b.nested_a
+
+        self.assertTrue(isinstance(b.nested_a, Everything.TestStructA))
+        self.assertEquals(a.some_int, a_out.some_int)
+        self.assertEquals(a.some_int8, a_out.some_int8)
+        self.assertEquals(a.some_double, a_out.some_double)
+        self.assertEquals(a.some_enum, a_out.some_enum)
 
         # Test assignment checking.
         self.assertRaises(TypeError, setattr, b, 'nested_a', 'a')
         self.assertRaises(TypeError, setattr, b, 'nested_a', Everything.TestStructB())
 
-        b_out = Everything.TestStructB()
-        b.clone(b_out)
-        self.assertEquals(b, b_out)
-
-
-# Plain-old-data boxed types
-
-    def testSimpleBoxedA(self):
-        a = Everything.TestSimpleBoxedA()
-        a.some_int = 42
-        a.some_int8 = 7
-        a.some_double = 3.14
-        a.some_enum = Everything.TestEnum.VALUE3
-
-        self.assertEquals(42, a.some_int)
-        self.assertEquals(7, a.some_int8)
-        self.assertAlmostEquals(3.14, a.some_double)
-        self.assertEquals(Everything.TestEnum.VALUE3, a.some_enum)
-
-        self.assertRaises(TypeError, setattr, a, 'some_int', 'a')
-        self.assertRaises(ValueError, setattr, a, 'some_int8', INT8_MIN-1)
-        self.assertRaises(ValueError, setattr, a, 'some_int8', INT8_MAX+1)
-
-        a_out = a.copy()
-        self.assertTrue(a.equals(a_out))
-        self.assertEquals(a, a_out)
-
-        # Test instance checking by passing a wrong instance.
-        self.assertRaises(TypeError, Everything.TestSimpleBoxedA.copy, 'a')
-        self.assertRaises(TypeError, Everything.TestSimpleBoxedA.copy, gobject.GObject())
-
-        # Test boxed as return value.
-        a_const = Everything.test_simple_boxed_a_const_return()
-        self.assertEquals(5, a_const.some_int)
-        self.assertEquals(6, a_const.some_int8)
-        self.assertAlmostEquals(7.0, a_const.some_double)
-
-    def testSimpleBoxedB(self):
-        a_const = Everything.test_simple_boxed_a_const_return()
-
-        b = Everything.TestSimpleBoxedB()
-        b.some_int = 42
-        b.nested_a = a_const.copy()
-
-        self.assertEquals(a_const, b.nested_a)
-
-        self.assertRaises(TypeError, setattr, b, 'nested_a', 'a')
-
-        b_out = b.copy()
-        self.assertEquals(b, b_out)
+    def testStructC(self):
+        self.assertRaises(TypeError, Everything.TestStructC)
 
 
 # GObject



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]