[pygobject/pygobject-2-28] Use PyGI type conversion (to fix foreign types) for signal callbacks.
- From: John Palmieri <johnp src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject/pygobject-2-28] Use PyGI type conversion (to fix foreign types) for signal callbacks.
- Date: Mon, 14 Feb 2011 23:18:02 +0000 (UTC)
commit a64b8fe89492ae47172979f1900686455cd1f9a8
Author: Laszlo Pandy <lpandy src gnome org>
Date: Mon Feb 14 19:36:27 2011 +0100
Use PyGI type conversion (to fix foreign types) for signal callbacks.
First attempt at patch to fix foreign types in signal callbacks.
Tests are not implemented yet.
https://bugzilla.gnome.org/show_bug.cgi?id=637601
gi/Makefile.am | 2 +
gi/gimodule.c | 1 +
gi/pygi-argument.c | 91 +++++++++++++++++
gi/pygi-argument.h | 2 +
gi/pygi-private.h | 1 +
gi/pygi-signal-closure.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++
gi/pygi-signal-closure.h | 46 +++++++++
gi/pygi.h | 28 +++++
gobject/pygobject.c | 24 ++++-
9 files changed, 436 insertions(+), 4 deletions(-)
---
diff --git a/gi/Makefile.am b/gi/Makefile.am
index a98993b..28825ab 100644
--- a/gi/Makefile.am
+++ b/gi/Makefile.am
@@ -54,6 +54,8 @@ _gi_la_SOURCES = \
pygi-private.h \
pygi-property.c \
pygi-property.h \
+ pygi-signal-closure.c \
+ pygi-signal-closure.h \
pygobject-external.h \
gimodule.c
diff --git a/gi/gimodule.c b/gi/gimodule.c
index f70d0f2..55c9d8b 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -367,6 +367,7 @@ static struct PyGI_API CAPI = {
pygi_type_import_by_g_type_real,
pygi_get_property_value_real,
pygi_set_property_value_real,
+ pygi_signal_closure_new_real,
pygi_register_foreign_struct_real,
};
diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index 8dd728d..9816738 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -1719,6 +1719,97 @@ _pygi_argument_to_object (GIArgument *arg,
return object;
}
+
+GIArgument
+_pygi_argument_from_g_value(const GValue *value,
+ GITypeInfo *type_info)
+{
+ GIArgument arg = { 0, };
+
+ GITypeTag type_tag = g_type_info_get_tag (type_info);
+ switch (type_tag) {
+ case GI_TYPE_TAG_BOOLEAN:
+ arg.v_boolean = g_value_get_boolean (value);
+ break;
+ case GI_TYPE_TAG_INT8:
+ case GI_TYPE_TAG_INT16:
+ case GI_TYPE_TAG_INT32:
+ case GI_TYPE_TAG_INT64:
+ arg.v_int = g_value_get_int (value);
+ break;
+ case GI_TYPE_TAG_UINT8:
+ case GI_TYPE_TAG_UINT16:
+ case GI_TYPE_TAG_UINT32:
+ case GI_TYPE_TAG_UINT64:
+ arg.v_uint = g_value_get_uint (value);
+ break;
+ case GI_TYPE_TAG_UNICHAR:
+ arg.v_uint32 = g_value_get_char (value);
+ break;
+ case GI_TYPE_TAG_FLOAT:
+ arg.v_float = g_value_get_float (value);
+ break;
+ case GI_TYPE_TAG_DOUBLE:
+ arg.v_double = g_value_get_double (value);
+ break;
+ case GI_TYPE_TAG_GTYPE:
+ arg.v_long = g_value_get_gtype (value);
+ break;
+ case GI_TYPE_TAG_UTF8:
+ case GI_TYPE_TAG_FILENAME:
+ arg.v_string = g_value_dup_string (value);
+ break;
+ case GI_TYPE_TAG_GLIST:
+ case GI_TYPE_TAG_GSLIST:
+ arg.v_pointer = g_value_get_pointer (value);
+ break;
+ case GI_TYPE_TAG_ARRAY:
+ case GI_TYPE_TAG_GHASH:
+ arg.v_pointer = g_value_get_boxed (value);
+ break;
+ case GI_TYPE_TAG_INTERFACE:
+ {
+ GIBaseInfo *info;
+ GIInfoType info_type;
+
+ info = g_type_info_get_interface (type_info);
+ info_type = g_base_info_get_type (info);
+
+ g_base_info_unref (info);
+
+ switch (info_type) {
+ case GI_INFO_TYPE_FLAGS:
+ case GI_INFO_TYPE_ENUM:
+ arg.v_long = g_value_get_enum (value);
+ break;
+ case GI_INFO_TYPE_INTERFACE:
+ case GI_INFO_TYPE_OBJECT:
+ arg.v_pointer = g_value_get_object (value);
+ break;
+ case GI_INFO_TYPE_BOXED:
+ case GI_INFO_TYPE_STRUCT:
+ case GI_INFO_TYPE_UNION:
+ if (G_VALUE_HOLDS(value, G_TYPE_BOXED)) {
+ arg.v_pointer = g_value_get_boxed (value);
+ } else {
+ arg.v_pointer = g_value_get_pointer (value);
+ }
+ break;
+ default:
+ g_warning("Converting of type '%s' is not implemented", g_info_type_to_string(info_type));
+ g_assert_not_reached();
+ }
+ break;
+ }
+ case GI_TYPE_TAG_ERROR:
+ case GI_TYPE_TAG_VOID:
+ g_critical("Converting of type '%s' is not implemented", g_type_tag_to_string(type_tag));
+ g_assert_not_reached();
+ }
+
+ return arg;
+}
+
void
_pygi_argument_release (GIArgument *arg,
GITypeInfo *type_info,
diff --git a/gi/pygi-argument.h b/gi/pygi-argument.h
index d932e8f..7224c75 100644
--- a/gi/pygi-argument.h
+++ b/gi/pygi-argument.h
@@ -55,6 +55,8 @@ PyObject* _pygi_argument_to_object (GIArgument *arg,
GITypeInfo *type_info,
GITransfer transfer);
+GIArgument _pygi_argument_from_g_value(const GValue *value,
+ GITypeInfo *type_info);
void _pygi_argument_release (GIArgument *arg,
GITypeInfo *type_info,
diff --git a/gi/pygi-private.h b/gi/pygi-private.h
index 3a14bc3..efe62c8 100644
--- a/gi/pygi-private.h
+++ b/gi/pygi-private.h
@@ -29,6 +29,7 @@
#include "pygi-callbacks.h"
#include "pygi-invoke.h"
#include "pygi-property.h"
+#include "pygi-signal-closure.h"
G_BEGIN_DECLS
#if PY_VERSION_HEX >= 0x03000000
diff --git a/gi/pygi-signal-closure.c b/gi/pygi-signal-closure.c
new file mode 100644
index 0000000..1482529
--- /dev/null
+++ b/gi/pygi-signal-closure.c
@@ -0,0 +1,245 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2011 Laszlo Pandy <lpandy src 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 Street, Fifth Floor, Boston, MA 02110-1301
+ * USA
+ */
+
+#include "pygi-private.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 GISignalInfo *
+_pygi_lookup_signal_from_g_type (GType g_type,
+ const gchar *signal_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) {
+ n_infos = g_object_info_get_n_signals ( (GIObjectInfo *) info);
+ for (i = 0; i < n_infos; i++) {
+ GISignalInfo *signal_info;
+
+ signal_info = g_object_info_get_signal ( (GIObjectInfo *) info, i);
+ g_assert (info != NULL);
+
+ if (strcmp (signal_name, g_base_info_get_name (signal_info)) == 0) {
+ g_base_info_unref (info);
+ return signal_info;
+ }
+
+ g_base_info_unref (signal_info);
+ }
+
+ g_base_info_unref (info);
+ }
+
+ parent = g_type_parent (g_type);
+ if (parent > 0)
+ return _pygi_lookup_signal_from_g_type (parent, signal_name);
+
+ return NULL;
+}
+
+static void
+pygi_signal_closure_invalidate(gpointer data,
+ GClosure *closure)
+{
+ PyGClosure *pc = (PyGClosure *)closure;
+ PyGILState_STATE state;
+
+ state = PyGILState_Ensure();
+ Py_XDECREF(pc->callback);
+ Py_XDECREF(pc->extra_args);
+ Py_XDECREF(pc->swap_data);
+ PyGILState_Release(state);
+
+ pc->callback = NULL;
+ pc->extra_args = NULL;
+ pc->swap_data = NULL;
+
+ g_base_info_unref (((PyGISignalClosure *) pc)->signal_info);
+ ((PyGISignalClosure *) pc)->signal_info = NULL;
+}
+
+static void
+pygi_signal_closure_marshal(GClosure *closure,
+ GValue *return_value,
+ guint n_param_values,
+ const GValue *param_values,
+ gpointer invocation_hint,
+ gpointer marshal_data)
+{
+ PyGILState_STATE state;
+ PyGClosure *pc = (PyGClosure *)closure;
+ PyObject *params, *ret = NULL;
+ guint i;
+ GISignalInfo *signal_info;
+ gint n_sig_info_args;
+ gint sig_info_highest_arg;
+
+ state = PyGILState_Ensure();
+
+ signal_info = ((PyGISignalClosure *)closure)->signal_info;
+ n_sig_info_args = g_callable_info_get_n_args(signal_info);
+ /* the first argument to a signal callback is instance,
+ but instance is not counted in the introspection data */
+ sig_info_highest_arg = n_sig_info_args + 1;
+ g_assert_cmpint(sig_info_highest_arg, ==, n_param_values);
+
+ /* construct Python tuple for the parameter values */
+ params = PyTuple_New(n_param_values);
+ for (i = 0; i < n_param_values; i++) {
+ /* swap in a different initial data for connect_object() */
+ if (i == 0 && G_CCLOSURE_SWAP_DATA(closure)) {
+ g_return_if_fail(pc->swap_data != NULL);
+ Py_INCREF(pc->swap_data);
+ PyTuple_SetItem(params, 0, pc->swap_data);
+
+ } else if (i == 0) {
+ PyObject *item = pyg_value_as_pyobject(¶m_values[i], FALSE);
+
+ if (!item) {
+ goto out;
+ }
+ PyTuple_SetItem(params, i, item);
+
+ } else if (i < sig_info_highest_arg) {
+ GIArgInfo arg_info;
+ GITypeInfo type_info;
+ GITransfer transfer;
+ GIArgument arg = { 0, };
+ PyObject *item = NULL;
+
+ g_callable_info_load_arg(signal_info, i - 1, &arg_info);
+ g_arg_info_load_type(&arg_info, &type_info);
+ transfer = g_arg_info_get_ownership_transfer(&arg_info);
+
+ arg = _pygi_argument_from_g_value(¶m_values[i], &type_info);
+ item = _pygi_argument_to_object(&arg, &type_info, transfer);
+
+ if (item == NULL) {
+ goto out;
+ }
+ PyTuple_SetItem(params, i, item);
+ }
+ }
+ /* params passed to function may have extra arguments */
+ if (pc->extra_args) {
+ PyObject *tuple = params;
+ params = PySequence_Concat(tuple, pc->extra_args);
+ Py_DECREF(tuple);
+ }
+ ret = PyObject_CallObject(pc->callback, params);
+ if (ret == NULL) {
+ if (pc->exception_handler)
+ pc->exception_handler(return_value, n_param_values, param_values);
+ else
+ PyErr_Print();
+ goto out;
+ }
+
+ if (return_value && pyg_value_from_pyobject(return_value, ret) != 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't convert return value to desired type");
+
+ if (pc->exception_handler)
+ pc->exception_handler(return_value, n_param_values, param_values);
+ else
+ PyErr_Print();
+ }
+ Py_DECREF(ret);
+
+ out:
+ Py_DECREF(params);
+ PyGILState_Release(state);
+}
+
+GClosure *
+pygi_signal_closure_new_real (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data)
+{
+ GClosure *closure = NULL;
+ PyGISignalClosure *pygi_closure = NULL;
+ GType g_type;
+ GISignalInfo *signal_info = NULL;
+ char *signal_name = g_strdup (sig_name);
+
+ g_return_val_if_fail(callback != NULL, NULL);
+
+ canonicalize_key(signal_name);
+
+ g_type = pyg_type_from_object ((PyObject *)instance);
+ signal_info = _pygi_lookup_signal_from_g_type (g_type, signal_name);
+
+ if (signal_info == NULL)
+ goto out;
+
+ closure = g_closure_new_simple(sizeof(PyGISignalClosure), NULL);
+ g_closure_add_invalidate_notifier(closure, NULL, pygi_signal_closure_invalidate);
+ g_closure_set_marshal(closure, pygi_signal_closure_marshal);
+
+ pygi_closure = (PyGISignalClosure *)closure;
+
+ pygi_closure->signal_info = signal_info;
+ Py_INCREF(callback);
+ pygi_closure->pyg_closure.callback = callback;
+
+ if (extra_args != NULL && extra_args != Py_None) {
+ Py_INCREF(extra_args);
+ if (!PyTuple_Check(extra_args)) {
+ PyObject *tmp = PyTuple_New(1);
+ PyTuple_SetItem(tmp, 0, extra_args);
+ extra_args = tmp;
+ }
+ pygi_closure->pyg_closure.extra_args = extra_args;
+ }
+ if (swap_data) {
+ Py_INCREF(swap_data);
+ pygi_closure->pyg_closure.swap_data = swap_data;
+ closure->derivative_flag = TRUE;
+ }
+
+out:
+ g_free (signal_name);
+
+ return closure;
+}
diff --git a/gi/pygi-signal-closure.h b/gi/pygi-signal-closure.h
new file mode 100644
index 0000000..4687f3f
--- /dev/null
+++ b/gi/pygi-signal-closure.h
@@ -0,0 +1,46 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (c) 2011 Laszlo Pandy <lpandy src gnome org>
+ *
+ * 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_SIGNAL_CLOSURE_H__
+#define __PYGI_SIGNAL_CLOSURE_H__
+
+#include "pygi.h"
+
+G_BEGIN_DECLS
+
+/* Private */
+typedef struct _PyGISignalClosure
+{
+ PyGClosure pyg_closure;
+ GISignalInfo *signal_info;
+} PyGISignalClosure;
+
+GClosure * pygi_signal_closure_new_real (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data);
+
+G_END_DECLS
+
+#endif /* __PYGI_SIGNAL_CLOSURE_H__ */
diff --git a/gi/pygi.h b/gi/pygi.h
index 2765b40..274af4d 100644
--- a/gi/pygi.h
+++ b/gi/pygi.h
@@ -71,6 +71,11 @@ struct PyGI_API {
gint (*set_property_value) (PyGObject *instance,
const gchar *attr_name,
PyObject *value);
+ GClosure * (*signal_closure_new) (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data);
void (*register_foreign_struct) (const char* namespace_,
const char* name,
PyGIArgOverrideToGIArgumentFunc to_func,
@@ -128,6 +133,19 @@ pygi_set_property_value (PyGObject *instance,
return PyGI_API->set_property_value(instance, attr_name, value);
}
+static inline GClosure *
+pygi_signal_closure_new (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data)
+{
+ if (_pygi_import() < 0) {
+ return NULL;
+ }
+ return PyGI_API->signal_closure_new(instance, sig_name, callback, extra_args, swap_data);
+}
+
static inline PyObject *
pygi_register_foreign_struct (const char* namespace_,
const char* name,
@@ -169,6 +187,16 @@ pygi_set_property_value (PyGObject *instance,
return -1;
}
+static inline GClosure *
+pygi_signal_closure_new (PyGObject *instance,
+ const gchar *sig_name,
+ PyObject *callback,
+ PyObject *extra_args,
+ PyObject *swap_data)
+{
+ return NULL;
+}
+
#endif /* ENABLE_INTROSPECTION */
#endif /* __PYGI_H__ */
diff --git a/gobject/pygobject.c b/gobject/pygobject.c
index a7b4505..3e77f99 100644
--- a/gobject/pygobject.c
+++ b/gobject/pygobject.c
@@ -1528,7 +1528,11 @@ pygobject_connect(PyGObject *self, PyObject *args)
extra_args = PySequence_GetSlice(args, 2, len);
if (extra_args == NULL)
return NULL;
- closure = pyg_closure_new(callback, extra_args, NULL);
+
+ closure = pygi_signal_closure_new(self, name, callback, extra_args, NULL);
+ if (closure == NULL)
+ closure = pyg_closure_new(callback, extra_args, NULL);
+
pygobject_watch_closure((PyObject *)self, closure);
handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail,
closure, FALSE);
@@ -1577,7 +1581,11 @@ pygobject_connect_after(PyGObject *self, PyObject *args)
extra_args = PySequence_GetSlice(args, 2, len);
if (extra_args == NULL)
return NULL;
- closure = pyg_closure_new(callback, extra_args, NULL);
+
+ closure = pygi_signal_closure_new(self, name, callback, extra_args, NULL);
+ if (closure == NULL)
+ closure = pyg_closure_new(callback, extra_args, NULL);
+
pygobject_watch_closure((PyObject *)self, closure);
handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail,
closure, TRUE);
@@ -1626,7 +1634,11 @@ pygobject_connect_object(PyGObject *self, PyObject *args)
extra_args = PySequence_GetSlice(args, 3, len);
if (extra_args == NULL)
return NULL;
- closure = pyg_closure_new(callback, extra_args, object);
+
+ closure = pygi_signal_closure_new(self, name, callback, extra_args, object);
+ if (closure == NULL)
+ closure = pyg_closure_new(callback, extra_args, object);
+
pygobject_watch_closure((PyObject *)self, closure);
handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail,
closure, FALSE);
@@ -1675,7 +1687,11 @@ pygobject_connect_object_after(PyGObject *self, PyObject *args)
extra_args = PySequence_GetSlice(args, 3, len);
if (extra_args == NULL)
return NULL;
- closure = pyg_closure_new(callback, extra_args, object);
+
+ closure = pygi_signal_closure_new(self, name, callback, extra_args, object);
+ if (closure == NULL)
+ closure = pyg_closure_new(callback, extra_args, object);
+
pygobject_watch_closure((PyObject *)self, closure);
handlerid = g_signal_connect_closure_by_id(self->obj, sigid, detail,
closure, TRUE);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]