[pygobject] Implement getting and setting properties using introspection information.
- From: Tomeu Vizoso <tomeuv src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject] Implement getting and setting properties using introspection information.
- Date: Tue, 27 Jul 2010 19:52:59 +0000 (UTC)
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]