[pygobject/pygobject-2-28] Use PyGI type conversion (to fix foreign types) for signal callbacks.



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(&param_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(&param_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]