[pygobject] Add marshalling coercion for Python classes and instances to GTypeClass



commit 778d05c93e079ba207a250b754bda9377cb47457
Author: Simon Feltman <sfeltman src gnome org>
Date:   Sun May 25 19:05:56 2014 -0700

    Add marshalling coercion for Python classes and instances to GTypeClass
    
    Automatically marshal Python GObject classes and instances to GTypeClass
    structs (GObjectClass). This allows usage of the GTypeClass methods by
    passing a Python GObject class or instance to the GTypeClass method.
    This is needed to support usage of GTypeClass methods since we don't
    manually bind GTypeClasses and they are not very well supported with
    introspection.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=685218

 gi/pygi-struct-marshal.c |   54 +++++++++++++++++++++++++++++++++---
 tests/Makefile.am        |    1 +
 tests/test_typeclass.py  |   67 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+), 5 deletions(-)
---
diff --git a/gi/pygi-struct-marshal.c b/gi/pygi-struct-marshal.c
index 412153b..9abaaae 100644
--- a/gi/pygi-struct-marshal.c
+++ b/gi/pygi-struct-marshal.c
@@ -431,6 +431,39 @@ arg_foreign_to_py_cleanup (PyGIInvokeState *state,
     }
 }
 
+static gboolean
+arg_type_class_from_py_marshal (PyGIInvokeState   *state,
+                                PyGICallableCache *callable_cache,
+                                PyGIArgCache      *arg_cache,
+                                PyObject          *py_arg,
+                                GIArgument        *arg,
+                                gpointer          *cleanup_data)
+{
+    GType gtype = pyg_type_from_object (py_arg);
+
+    if (G_TYPE_IS_CLASSED (gtype)) {
+        arg->v_pointer = g_type_class_ref (gtype);
+        *cleanup_data = arg->v_pointer;
+        return TRUE;
+    } else {
+        PyErr_Format (PyExc_TypeError,
+                      "Unable to retrieve a GObject type class from \"%s\".",
+                      Py_TYPE(py_arg)->tp_name);
+        return FALSE;
+    }
+}
+
+static void
+arg_type_class_from_py_cleanup (PyGIInvokeState *state,
+                                PyGIArgCache    *arg_cache,
+                                PyObject        *py_arg,
+                                gpointer         data,
+                                gboolean         was_processed)
+{
+    if (was_processed) {
+        g_type_class_unref (data);
+    }
+}
 
 static void
 arg_struct_from_py_setup (PyGIArgCache     *arg_cache,
@@ -439,12 +472,23 @@ arg_struct_from_py_setup (PyGIArgCache     *arg_cache,
 {
     PyGIInterfaceCache *iface_cache = (PyGIInterfaceCache *)arg_cache;
     iface_cache->is_foreign = g_struct_info_is_foreign ( (GIStructInfo*)iface_info);
-    arg_cache->from_py_marshaller = arg_struct_from_py_marshal_adapter;
 
-    if (iface_cache->g_type == G_TYPE_VALUE)
-        arg_cache->from_py_cleanup = pygi_arg_gvalue_from_py_cleanup;
-    else if (iface_cache->is_foreign)
-        arg_cache->from_py_cleanup = arg_foreign_from_py_cleanup;
+    if (g_struct_info_is_gtype_struct ((GIStructInfo*)iface_info)) {
+        arg_cache->from_py_marshaller = arg_type_class_from_py_marshal;
+        /* Since we always add a ref in the marshalling, only unref the
+         * GTypeClass when we don't transfer ownership. */
+        if (transfer == GI_TRANSFER_NOTHING) {
+            arg_cache->from_py_cleanup = arg_type_class_from_py_cleanup;
+        }
+
+    } else {
+        arg_cache->from_py_marshaller = arg_struct_from_py_marshal_adapter;
+
+        if (iface_cache->g_type == G_TYPE_VALUE)
+            arg_cache->from_py_cleanup = pygi_arg_gvalue_from_py_cleanup;
+        else if (iface_cache->is_foreign)
+            arg_cache->from_py_cleanup = arg_foreign_from_py_cleanup;
+    }
 }
 
 static void
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5529abc..c0f34f8 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -101,6 +101,7 @@ EXTRA_DIST = \
        test_source.py \
        test_subprocess.py \
        test_thread.py \
+       test_typeclass.py \
        test_everything.py \
        test_gi.py \
        test_gdbus.py \
diff --git a/tests/test_typeclass.py b/tests/test_typeclass.py
new file mode 100644
index 0000000..300fe81
--- /dev/null
+++ b/tests/test_typeclass.py
@@ -0,0 +1,67 @@
+# -*- Mode: Python; py-indent-offset: 4 -*-
+# vim: tabstop=4 shiftwidth=4 expandtab
+#
+# test_typeclass.py: Tests for GTypeClass related methods and marshalling.
+#
+# Copyright (C) 2014 Simon Feltman <sfeltman gnome org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+# USA
+
+import unittest
+
+from gi.repository import GObject
+from gi.repository import GIMarshallingTests
+
+
+class TestCoercion(unittest.TestCase):
+    def test_coerce_from_class(self):
+        prop = GObject.ObjectClass.find_property(GIMarshallingTests.PropertiesObject,
+                                                 'some-int')
+
+        self.assertIsInstance(prop, GObject.GParamSpec)
+        self.assertEqual(prop.name, 'some-int')
+        self.assertEqual(prop.value_type, GObject.TYPE_INT)
+        self.assertEqual(prop.owner_type,
+                         GIMarshallingTests.PropertiesObject.__gtype__)
+
+    def test_coerce_from_gtype(self):
+        gtype = GIMarshallingTests.PropertiesObject.__gtype__
+        prop = GObject.ObjectClass.find_property(gtype, 'some-int')
+
+        self.assertIsInstance(prop, GObject.GParamSpec)
+        self.assertEqual(prop.name, 'some-int')
+        self.assertEqual(prop.value_type, GObject.TYPE_INT)
+        self.assertEqual(prop.owner_type, gtype)
+
+    def test_coerce_from_instance(self):
+        obj = GIMarshallingTests.PropertiesObject()
+        prop = GObject.ObjectClass.find_property(obj, 'some-int')
+
+        self.assertIsInstance(prop, GObject.GParamSpec)
+        self.assertEqual(prop.name, 'some-int')
+        self.assertEqual(prop.value_type, GObject.TYPE_INT)
+        self.assertEqual(prop.owner_type, obj.__gtype__)
+
+    def test_marshalling_error(self):
+        with self.assertRaises(TypeError):
+            GObject.ObjectClass.find_property(object, 'some-int')
+
+        with self.assertRaises(TypeError):
+            GObject.ObjectClass.find_property(42, 'some-int')
+
+
+if __name__ == '__main__':
+    unittest.main()


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