[pygobject] Implement getting and setting properties using introspection information.



commit 6655a79b2f13fe417aefdf6aebab0f2d6162ba00
Author: Tomeu Vizoso <tomeu vizoso collabora co uk>
Date:   Tue Jul 27 21:52:49 2010 +0200

    Implement getting and setting properties using introspection information.
    
    This allows us to use information not present in GObject such as
    transfer and element types.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=620808

 gi/Makefile.am           |    2 +
 gi/gimodule.c            |    2 +
 gi/pygi-argument.c       |    2 +
 gi/pygi-private.h        |    1 +
 gi/pygi-property.c       |  226 ++++++++++++++++++++++++++++++++++++++++++++++
 gi/pygi-property.h       |   39 ++++++++
 gi/pygi.h                |   41 +++++++++
 gobject/pygobject.c      |    9 ++
 tests/test_everything.py |   19 ++++
 9 files changed, 341 insertions(+), 0 deletions(-)
---
diff --git a/gi/Makefile.am b/gi/Makefile.am
index fc57edb..d0541bb 100644
--- a/gi/Makefile.am
+++ b/gi/Makefile.am
@@ -50,6 +50,8 @@ _gi_la_SOURCES = \
 	pygi-callbacks.h \
 	pygi.h \
 	pygi-private.h \
+	pygi-property.c \
+	pygi-property.h \
 	pygobject-external.h \
 	gimodule.c
 
diff --git a/gi/gimodule.c b/gi/gimodule.c
index df0db7a..56ae7a3 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -241,6 +241,8 @@ static PyMethodDef _pygi_functions[] = {
 
 static struct PyGI_API CAPI = {
   pygi_type_import_by_g_type_real,
+  pygi_get_property_value_real,
+  pygi_set_property_value_real,
   pygi_register_foreign_struct_real,
 };
 
diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index 3b7d882..24f996e 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -1499,9 +1499,11 @@ _pygi_argument_to_object (GArgument  *arg,
 
             key_type_info = g_type_info_get_param_type (type_info, 0);
             g_assert (key_type_info != NULL);
+            g_assert (g_type_info_get_tag (key_type_info) != GI_TYPE_TAG_VOID);
 
             value_type_info = g_type_info_get_param_type (type_info, 1);
             g_assert (value_type_info != NULL);
+            g_assert (g_type_info_get_tag (value_type_info) != GI_TYPE_TAG_VOID);
 
             item_transfer = transfer == GI_TRANSFER_CONTAINER ? GI_TRANSFER_NOTHING : transfer;
 
diff --git a/gi/pygi-private.h b/gi/pygi-private.h
index 0ff5df7..9435c9b 100644
--- a/gi/pygi-private.h
+++ b/gi/pygi-private.h
@@ -28,6 +28,7 @@
 #include "pygi-closure.h"
 #include "pygi-callbacks.h"
 #include "pygi-invoke.h"
+#include "pygi-property.h"
 
 G_BEGIN_DECLS
 
diff --git a/gi/pygi-property.c b/gi/pygi-property.c
new file mode 100644
index 0000000..e91d419
--- /dev/null
+++ b/gi/pygi-property.c
@@ -0,0 +1,226 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2010  Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "pygi-private.h"
+
+#include <girepository.h>
+
+/* Copied from glib */
+static void
+canonicalize_key (gchar *key)
+{
+    gchar *p;
+
+    for (p = key; *p != 0; p++)
+    {
+        gchar c = *p;
+
+        if (c != '-' &&
+            (c < '0' || c > '9') &&
+            (c < 'A' || c > 'Z') &&
+            (c < 'a' || c > 'z'))
+                *p = '-';
+    }
+}
+
+static GIPropertyInfo *
+_pygi_lookup_property_from_g_type (GType g_type, const gchar *attr_name)
+{
+    GIRepository *repository;
+    GIBaseInfo *info;
+    gssize n_infos;
+    gssize i;
+    GType parent;
+
+    repository = g_irepository_get_default();
+    info = g_irepository_find_by_gtype (repository, g_type);
+    if (info == NULL) {
+        return NULL;
+    }
+
+    n_infos = g_object_info_get_n_properties ( (GIObjectInfo *) info);
+    for (i = 0; i < n_infos; i++) {
+        GIPropertyInfo *property_info;
+
+        property_info = g_object_info_get_property ( (GIObjectInfo *) info, i);
+        g_assert (info != NULL);
+
+        if (strcmp (attr_name, g_base_info_get_name (property_info)) == 0) {
+            g_base_info_unref (info);
+            return property_info;
+        }
+
+        g_base_info_unref (property_info);
+    }
+
+    g_base_info_unref (info);
+
+    parent = g_type_parent (g_type);
+    if (parent > 0)
+        return _pygi_lookup_property_from_g_type (parent, attr_name);
+
+    return NULL;
+}
+
+PyObject *
+pygi_get_property_value_real (PyGObject *instance,
+                              const gchar *attr_name)
+{
+    GType g_type;
+    GIPropertyInfo *property_info = NULL;
+    char *property_name = g_strdup (attr_name);
+    GParamSpec *pspec = NULL;
+    GValue value = { 0, };
+    GArgument arg = { 0, };
+    PyObject *py_value = NULL;
+    GITypeInfo *type_info = NULL;
+    GITransfer transfer;
+
+    canonicalize_key (property_name);
+
+    g_type = pyg_type_from_object ((PyObject *)instance);
+    property_info = _pygi_lookup_property_from_g_type (g_type, property_name);
+
+    if (property_info == NULL)
+        goto out;
+
+    pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (instance->obj),
+                                          attr_name);
+    if (pspec == NULL)
+        goto out;
+
+    g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+    g_object_get_property (instance->obj, attr_name, &value);
+
+    type_info = g_property_info_get_type (property_info);
+    transfer = g_property_info_get_ownership_transfer (property_info);
+
+    // FIXME: Lots of types still unhandled
+    if (g_type_info_is_pointer (type_info)) {
+        arg.v_pointer = g_value_peek_pointer (&value);
+    } else {
+        GITypeTag type_tag = g_type_info_get_tag (type_info);
+        switch (type_tag) {
+            case GI_TYPE_TAG_INT32:
+                arg.v_int = g_value_get_int (&value);
+                break;
+            case GI_TYPE_TAG_UINT32:
+                arg.v_uint = g_value_get_uint (&value);
+                break;
+            case GI_TYPE_TAG_BOOLEAN:
+                arg.v_boolean = g_value_get_boolean (&value);
+                break;
+            case GI_TYPE_TAG_INTERFACE:
+                arg.v_pointer = g_value_get_object (&value);
+                break;
+            default:
+                PyErr_Format (PyExc_NotImplementedError,
+                              "Retrieving properties of type %s is not implemented",
+                              g_type_tag_to_string (g_type_info_get_tag (type_info)));
+                goto out;
+        }
+    }
+
+    py_value = _pygi_argument_to_object (&arg, type_info, transfer);
+
+out:
+    g_free (property_name);
+    if (property_info != NULL)
+        g_base_info_unref (property_info);
+    if (type_info != NULL)
+        g_base_info_unref (type_info);
+
+    return py_value;
+}
+
+gint
+pygi_set_property_value_real (PyGObject *instance,
+                              const gchar *attr_name,
+                              PyObject *py_value)
+{
+    GType g_type;
+    GIPropertyInfo *property_info = NULL;
+    char *property_name = g_strdup (attr_name);
+    GITypeInfo *type_info = NULL;
+    GITypeTag type_tag;
+    GITransfer transfer;
+    GValue value = { 0, };
+    GArgument arg = { 0, };
+    GParamSpec *pspec = NULL;
+    gint ret_value = -1;
+
+    canonicalize_key (property_name);
+
+    g_type = pyg_type_from_object ((PyObject *)instance);
+    property_info = _pygi_lookup_property_from_g_type (g_type, property_name);
+
+    if (property_info == NULL)
+        goto out;
+
+    pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (instance->obj),
+                                          attr_name);
+    if (pspec == NULL)
+        goto out;
+
+    if (! (pspec->flags & G_PARAM_WRITABLE))
+        goto out;
+
+    type_info = g_property_info_get_type (property_info);
+    transfer = g_property_info_get_ownership_transfer (property_info);
+    arg = _pygi_argument_from_object (py_value, type_info, transfer);
+
+    g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+    // FIXME: Lots of types still unhandled
+    type_tag = g_type_info_get_tag (type_info);
+    switch (type_tag) {
+        case GI_TYPE_TAG_GHASH:
+            g_value_set_boxed (&value, arg.v_pointer);
+            break;
+        case GI_TYPE_TAG_GLIST:
+            g_value_set_pointer (&value, arg.v_pointer);
+            break;
+        case GI_TYPE_TAG_INT32:
+            g_value_set_int (&value, arg.v_int);
+            break;
+        default:
+            PyErr_Format (PyExc_NotImplementedError,
+                          "Setting properties of type %s is not implemented",
+                          g_type_tag_to_string (g_type_info_get_tag (type_info)));
+            goto out;
+    }
+
+    g_object_set_property (instance->obj, attr_name, &value);
+
+    ret_value = 0;
+
+out:
+    g_free (property_name);
+    if (property_info != NULL)
+        g_base_info_unref (property_info);
+    if (type_info != NULL)
+        g_base_info_unref (type_info);
+
+    return ret_value;
+}
+
diff --git a/gi/pygi-property.h b/gi/pygi-property.h
new file mode 100644
index 0000000..31d0e42
--- /dev/null
+++ b/gi/pygi-property.h
@@ -0,0 +1,39 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2010  Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef __PYGI_PROPERTY_H__
+#define __PYGI_PROPERTY_H__
+
+#include <Python.h>
+#include <girepository.h>
+
+#include "pygi.h"
+
+PyObject *pygi_get_property_value_real (PyGObject *instance,
+                                        const gchar *attr_name);
+
+gint pygi_set_property_value_real (PyGObject *instance,
+                                   const gchar *attr_name,
+                                   PyObject *py_value);
+
+#endif /* __PYGI_PROPERTY_H__ */
diff --git a/gi/pygi.h b/gi/pygi.h
index 92b7bae..96a0dde 100644
--- a/gi/pygi.h
+++ b/gi/pygi.h
@@ -62,6 +62,11 @@ typedef PyObject * (*PyGIArgOverrideReleaseFunc) (GITypeInfo *type_info,
 
 struct PyGI_API {
     PyObject* (*type_import_by_g_type) (GType g_type);
+    PyObject* (*get_property_value) (PyGObject *instance,
+                                     const gchar *attr_name);
+    gint (*set_property_value) (PyGObject *instance,
+                                const gchar *attr_name,
+                                PyObject *value);
     void (*register_foreign_struct) (const char* namespace_,
                                      const char* name,
                                      PyGIArgOverrideToGArgumentFunc to_func,
@@ -96,6 +101,27 @@ pygi_type_import_by_g_type (GType g_type)
 }
 
 static inline PyObject *
+pygi_get_property_value (PyGObject *instance,
+                         const gchar *attr_name)
+{
+    if (_pygi_import() < 0) {
+        return NULL;
+    }
+    return PyGI_API->get_property_value(instance, attr_name);
+}
+
+static inline gint
+pygi_set_property_value (PyGObject *instance,
+                         const gchar *attr_name,
+                         PyObject *value)
+{
+    if (_pygi_import() < 0) {
+        return -1;
+    }
+    return PyGI_API->set_property_value(instance, attr_name, value);
+}
+
+static inline PyObject *
 pygi_register_foreign_struct (const char* namespace_,
                               const char* name,
                               PyGIArgOverrideToGArgumentFunc to_func,
@@ -121,6 +147,21 @@ pygi_type_import_by_g_type (GType g_type)
     return NULL;
 }
 
+static inline PyObject *
+pygi_get_property_value (PyGObject *instance,
+                         const gchar *attr_name)
+{
+    return -1;
+}
+
+static inline gint
+pygi_set_property_value (PyGObject *instance,
+                         const gchar *attr_name,
+                         PyObject *value)
+{
+    return -1;
+}
+
 #endif /* ENABLE_INTROSPECTION */
 
 #endif /* __PYGI_H__ */
diff --git a/gobject/pygobject.c b/gobject/pygobject.c
index c37c8a8..b6bd9ca 100644
--- a/gobject/pygobject.c
+++ b/gobject/pygobject.c
@@ -288,6 +288,12 @@ PyGProps_getattro(PyGProps *self, PyObject *attr)
 	return build_parameter_list(class);
     }
 
+    if (self->pygobject != NULL) {
+        ret = pygi_get_property_value (self->pygobject, attr_name);
+        if (ret != NULL)
+            return ret;
+    }
+
     pspec = g_object_class_find_property(class, attr_name);
     g_type_class_unref(class);
 
@@ -380,6 +386,9 @@ PyGProps_setattro(PyGProps *self, PyObject *attr, PyObject *pvalue)
         return -1;
     }
 
+    if (pygi_set_property_value (self->pygobject, attr_name, pvalue) == 0)
+        return 0;
+
     obj = self->pygobject->obj;
     pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), attr_name);
     if (!pspec) {
diff --git a/tests/test_everything.py b/tests/test_everything.py
index 66aa889..0b7455a 100644
--- a/tests/test_everything.py
+++ b/tests/test_everything.py
@@ -307,3 +307,22 @@ class TestCallbacks(unittest.TestCase):
     def testCallbackNone(self):
         # make sure this doesn't assert or crash
         Everything.test_simple_callback(None)
+
+
+class TestProperties(unittest.TestCase):
+
+    def test_hash_table(self):
+        object_ = Everything.TestObj()
+        self.assertEquals(object_.props.hash_table, None)
+
+        object_.props.hash_table = {'mec': 56}
+        self.assertTrue(isinstance(object_.props.hash_table, dict))
+        self.assertEquals(object_.props.hash_table.items()[0], ('mec', 56))
+
+    def test_list(self):
+        object_ = Everything.TestObj()
+        self.assertEquals(object_.props.list, [])
+
+        object_.props.list = ['1', '2', '3']
+        self.assertTrue(isinstance(object_.props.list, list))
+        self.assertEquals(object_.props.list, ['1', '2', '3'])



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