[pygobject] cache refactoring: Move GObject arg setup and marshaling into new file



commit 4dcaa2b988239e01224994098c3e7cbe8b455fe0
Author: Simon Feltman <sfeltman src gnome org>
Date:   Sat Oct 12 16:40:58 2013 -0700

    cache refactoring: Move GObject arg setup and marshaling into new file
    
    Move GObject argument cache setup and marshaling fragments into
    isolated file: pygi-object.c.
    Break GIInterfaceCache creation and setup into API for interface based
    argument cache usage.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=709700

 gi/Makefile.am            |    2 +
 gi/pygi-argument.c        |    7 +-
 gi/pygi-cache.c           |  183 ++++++++++++++----------------
 gi/pygi-cache.h           |    7 +
 gi/pygi-marshal-cleanup.c |   27 -----
 gi/pygi-marshal-cleanup.h |   10 --
 gi/pygi-marshal-from-py.c |  123 --------------------
 gi/pygi-marshal-from-py.h |   12 --
 gi/pygi-marshal-to-py.c   |   31 -----
 gi/pygi-marshal-to-py.h   |    8 --
 gi/pygi-object.c          |  273 +++++++++++++++++++++++++++++++++++++++++++++
 gi/pygi-object.h          |   46 ++++++++
 12 files changed, 416 insertions(+), 313 deletions(-)
---
diff --git a/gi/Makefile.am b/gi/Makefile.am
index d19a19d..3d153f4 100644
--- a/gi/Makefile.am
+++ b/gi/Makefile.am
@@ -111,6 +111,8 @@ _gi_la_SOURCES = \
        pygi-array.h \
        pygi-error.c \
        pygi-error.h \
+       pygi-object.c \
+       pygi-object.h \
        pygi-hashtable.c \
        pygi-hashtable.h
 _gi_la_CFLAGS = \
diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index c58afa3..4624e0f 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -34,6 +34,7 @@
 #include "pygi-marshal-from-py.h"
 #include "pygi-marshal-to-py.h"
 #include "pygi-basictype.h"
+#include "pygi-object.h"
 
 
 static gboolean
@@ -1063,7 +1064,7 @@ array_success:
                 case GI_INFO_TYPE_INTERFACE:
                 case GI_INFO_TYPE_OBJECT:
                     /* An error within this call will result in a NULL arg */
-                    _pygi_marshal_from_py_gobject_out_arg (object, &arg, transfer);
+                    pygi_arg_gobject_out_arg_from_py (object, &arg, transfer);
                     break;
 
                 default:
@@ -1436,10 +1437,10 @@ _pygi_argument_to_object (GIArgument  *arg,
                             transfer == GI_TRANSFER_NOTHING &&
                             g_object_is_floating (arg->v_pointer)) {
                         g_object_ref (arg->v_pointer);
-                        object = _pygi_marshal_to_py_object (arg, GI_TRANSFER_EVERYTHING);
+                        object = pygi_arg_gobject_to_py (arg, GI_TRANSFER_EVERYTHING);
                         g_object_force_floating (arg->v_pointer);
                     } else {
-                        object = _pygi_marshal_to_py_object (arg, transfer);
+                        object = pygi_arg_gobject_to_py (arg, transfer);
                     }
 
                     break;
diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c
index aded025..f26df8c 100644
--- a/gi/pygi-cache.c
+++ b/gi/pygi-cache.c
@@ -33,15 +33,7 @@
 #include "pygi-array.h"
 #include "pygi-closure.h"
 #include "pygi-error.h"
-
-
-PyGIArgCache * _arg_cache_new_for_interface (GIInterfaceInfo *iface_info,
-                                             GITypeInfo *type_info,
-                                             GIArgInfo *arg_info,
-                                             GITransfer transfer,
-                                             PyGIDirection direction,
-                                             /* will be removed */
-                                             PyGICallableCache *callable_cache);
+#include "pygi-object.h"
 
 
 /* _arg_info_default_value
@@ -100,32 +92,6 @@ pygi_arg_base_setup (PyGIArgCache *arg_cache,
     return TRUE;
 }
 
-
-gboolean
-pygi_arg_interface_setup (PyGIInterfaceCache *iface_cache,
-                          GITypeInfo         *type_info,
-                          GIArgInfo          *arg_info,    /* may be NULL for return arguments */
-                          GITransfer          transfer,
-                          PyGIDirection       direction,
-                          GIInterfaceInfo    *iface_info)
-{
-    if (!pygi_arg_base_setup ((PyGIArgCache *)iface_cache,
-                              type_info,
-                              arg_info,
-                              transfer,
-                              direction)) {
-        return FALSE;
-    }
-
-    g_base_info_ref ( (GIBaseInfo *)iface_info);
-    iface_cache->interface_info = iface_info;
-    iface_cache->arg_cache.type_tag = GI_TYPE_TAG_INTERFACE;
-
-    return TRUE;
-}
-
-
-/* cleanup */
 void
 _pygi_arg_cache_free (PyGIArgCache *cache)
 {
@@ -140,6 +106,26 @@ _pygi_arg_cache_free (PyGIArgCache *cache)
         g_slice_free (PyGIArgCache, cache);
 }
 
+void
+_pygi_callable_cache_free (PyGICallableCache *cache)
+{
+    if (cache == NULL)
+        return;
+
+    g_slist_free (cache->to_py_args);
+    g_slist_free (cache->arg_name_list);
+    g_hash_table_destroy (cache->arg_name_hash);
+    g_ptr_array_unref (cache->args_cache);
+
+    if (cache->return_cache != NULL)
+        _pygi_arg_cache_free (cache->return_cache);
+
+    g_slice_free (PyGICallableCache, cache);
+}
+
+
+/* PyGIInterfaceCache */
+
 static void
 _interface_cache_free_func (PyGIInterfaceCache *cache)
 {
@@ -153,50 +139,70 @@ _interface_cache_free_func (PyGIInterfaceCache *cache)
     }
 }
 
-
-static void
-_sequence_cache_free_func (PyGISequenceCache *cache)
+gboolean
+pygi_arg_interface_setup (PyGIInterfaceCache *iface_cache,
+                          GITypeInfo         *type_info,
+                          GIArgInfo          *arg_info,    /* may be NULL for return arguments */
+                          GITransfer          transfer,
+                          PyGIDirection       direction,
+                          GIInterfaceInfo    *iface_info)
 {
-    if (cache != NULL) {
-        _pygi_arg_cache_free (cache->item_cache);
-        g_slice_free (PyGISequenceCache, cache);
+    if (!pygi_arg_base_setup ((PyGIArgCache *)iface_cache,
+                              type_info,
+                              arg_info,
+                              transfer,
+                              direction)) {
+        return FALSE;
     }
-}
 
-void
-_pygi_callable_cache_free (PyGICallableCache *cache)
-{
-    if (cache == NULL)
-        return;
+    ( (PyGIArgCache *)iface_cache)->destroy_notify = (GDestroyNotify)_interface_cache_free_func;
 
-    g_slist_free (cache->to_py_args);
-    g_slist_free (cache->arg_name_list);
-    g_hash_table_destroy (cache->arg_name_hash);
-    g_ptr_array_unref (cache->args_cache);
+    g_base_info_ref ( (GIBaseInfo *)iface_info);
+    iface_cache->interface_info = iface_info;
+    iface_cache->arg_cache.type_tag = GI_TYPE_TAG_INTERFACE;
+    iface_cache->type_name = _pygi_g_base_info_get_fullname (iface_info);
+    iface_cache->g_type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *)iface_info);
+    iface_cache->py_type = _pygi_type_import_by_gi_info ( (GIBaseInfo *) iface_info);
 
-    if (cache->return_cache != NULL)
-        _pygi_arg_cache_free (cache->return_cache);
+    if (iface_cache->py_type == NULL) {
+        return FALSE;
+    }
 
-    g_slice_free (PyGICallableCache, cache);
+    return TRUE;
 }
 
-/* cache generation */
-
-static PyGIInterfaceCache *
-_interface_cache_new (GIInterfaceInfo *iface_info)
+PyGIArgCache *
+pygi_arg_interface_new_from_info (GITypeInfo         *type_info,
+                                  GIArgInfo          *arg_info,    /* may be NULL for return arguments */
+                                  GITransfer          transfer,
+                                  PyGIDirection       direction,
+                                  GIInterfaceInfo    *iface_info)
 {
     PyGIInterfaceCache *ic;
 
     ic = g_slice_new0 (PyGIInterfaceCache);
-    ( (PyGIArgCache *)ic)->destroy_notify = (GDestroyNotify)_interface_cache_free_func;
-    ic->g_type = g_registered_type_info_get_g_type ( (GIRegisteredTypeInfo *)iface_info);
-    ic->py_type = _pygi_type_import_by_gi_info ( (GIBaseInfo *) iface_info);
-
-    if (ic->py_type == NULL)
+    if (!pygi_arg_interface_setup (ic,
+                                   type_info,
+                                   arg_info,
+                                   transfer,
+                                   direction,
+                                   iface_info)) {
+        _pygi_arg_cache_free ((PyGIArgCache *)ic);
         return NULL;
+    }
 
-    ic->type_name = _pygi_g_base_info_get_fullname (iface_info);
-    return ic;
+    return (PyGIArgCache *)ic;
+}
+
+/* PyGISequenceCache */
+
+static void
+_sequence_cache_free_func (PyGISequenceCache *cache)
+{
+    if (cache != NULL) {
+        _pygi_arg_cache_free (cache->item_cache);
+        g_slice_free (PyGISequenceCache, cache);
+    }
 }
 
 gboolean
@@ -287,22 +293,6 @@ _arg_cache_to_py_interface_struct_setup (PyGIArgCache *arg_cache,
 }
 
 static void
-_arg_cache_from_py_interface_object_setup (PyGIArgCache *arg_cache,
-                                           GITransfer transfer)
-{
-    arg_cache->from_py_marshaller = _pygi_marshal_from_py_interface_object;
-    arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_object;
-}
-
-static void
-_arg_cache_to_py_interface_object_setup (PyGIArgCache *arg_cache,
-                                         GITransfer transfer)
-{
-    arg_cache->to_py_marshaller = _pygi_marshal_to_py_interface_object_cache_adapter;
-    arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_object;
-}
-
-static void
 _arg_cache_from_py_interface_enum_setup (PyGIArgCache *arg_cache,
                                          GITransfer transfer)
 {
@@ -331,7 +321,7 @@ _arg_cache_to_py_interface_flags_setup (PyGIArgCache *arg_cache,
 }
 
 
-PyGIArgCache *
+static PyGIArgCache *
 _arg_cache_new_for_interface (GIInterfaceInfo   *iface_info,
                               GITypeInfo        *type_info,
                               GIArgInfo         *arg_info,
@@ -354,11 +344,22 @@ _arg_cache_new_for_interface (GIInterfaceInfo   *iface_info,
                                                     iface_info,
                                                     callable_cache);
         }
+        case GI_INFO_TYPE_OBJECT:
+        case GI_INFO_TYPE_INTERFACE:
+            return pygi_arg_gobject_new_from_info (type_info,
+                                                   arg_info,
+                                                   transfer,
+                                                   direction,
+                                                   iface_info);
         default:
             ;  /* pass through to old model of setup */
     }
 
-    arg_cache = (PyGIArgCache *)_interface_cache_new (iface_info);
+    arg_cache = pygi_arg_interface_new_from_info (type_info,
+                                                  arg_info,
+                                                  transfer,
+                                                  direction,
+                                                  iface_info);
     if (arg_cache == NULL)
         return NULL;
 
@@ -383,15 +384,6 @@ _arg_cache_new_for_interface (GIInterfaceInfo   *iface_info,
                                                         iface_info,
                                                         transfer);
             break;
-        case GI_INFO_TYPE_OBJECT:
-        case GI_INFO_TYPE_INTERFACE:
-            if (direction & PYGI_DIRECTION_FROM_PYTHON)
-               _arg_cache_from_py_interface_object_setup (arg_cache, transfer);
-
-            if (direction & PYGI_DIRECTION_TO_PYTHON)
-               _arg_cache_to_py_interface_object_setup (arg_cache, transfer);
-
-            break;
         case GI_INFO_TYPE_ENUM:
             if (direction & PYGI_DIRECTION_FROM_PYTHON)
                _arg_cache_from_py_interface_enum_setup (arg_cache, transfer);
@@ -412,13 +404,6 @@ _arg_cache_new_for_interface (GIInterfaceInfo   *iface_info,
             g_assert_not_reached ();
     }
 
-    pygi_arg_interface_setup ((PyGIInterfaceCache *)arg_cache,
-                              type_info,
-                              arg_info,
-                              transfer,
-                              direction,
-                              iface_info);
-
     return arg_cache;
 }
 
diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h
index d20cb9f..3cbe2b6 100644
--- a/gi/pygi-cache.h
+++ b/gi/pygi-cache.h
@@ -214,6 +214,13 @@ pygi_arg_sequence_setup (PyGISequenceCache  *sc,
                          GITransfer          transfer,
                          PyGIDirection       direction);
 
+PyGIArgCache *
+pygi_arg_interface_new_from_info (GITypeInfo         *type_info,
+                                  GIArgInfo          *arg_info,     /* may be NULL for return arguments */
+                                  GITransfer          transfer,
+                                  PyGIDirection       direction,
+                                  GIInterfaceInfo    *iface_info);
+
 PyGIArgCache * _arg_cache_alloc (void);
 PyGIArgCache * _arg_cache_new (GITypeInfo *type_info,
                                GIArgInfo *arg_info,
diff --git a/gi/pygi-marshal-cleanup.c b/gi/pygi-marshal-cleanup.c
index 0376e37..0bbfab6 100644
--- a/gi/pygi-marshal-cleanup.c
+++ b/gi/pygi-marshal-cleanup.c
@@ -206,33 +206,6 @@ pygi_marshal_cleanup_args_to_py_parameter_fail (PyGIInvokeState   *state,
     state->failed = TRUE;
 }
 
-void
-_pygi_marshal_cleanup_from_py_interface_object (PyGIInvokeState *state,
-                                                PyGIArgCache    *arg_cache,
-                                                PyObject        *py_arg,
-                                                gpointer         data,
-                                                gboolean         was_processed)
-{
-    /* If we processed the parameter but fail before invoking the method,
-       we need to remove the ref we added */
-    if (was_processed && state->failed && data != NULL &&
-            arg_cache->transfer == GI_TRANSFER_EVERYTHING)
-        g_object_unref (G_OBJECT(data));
-}
-
-void
-_pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state,
-                                              PyGIArgCache    *arg_cache,
-                                              PyObject        *dummy,
-                                              gpointer         data,
-                                              gboolean         was_processed)
-{
-    /* If we error out and the object is not marshalled into a PyGObject
-       we must take care of removing the ref */
-    if (!was_processed && arg_cache->transfer == GI_TRANSFER_EVERYTHING)
-        g_object_unref (G_OBJECT(data));
-}
-
 void 
 _pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state,
                                                        PyGIArgCache    *arg_cache,
diff --git a/gi/pygi-marshal-cleanup.h b/gi/pygi-marshal-cleanup.h
index 530f5f3..2463cab 100644
--- a/gi/pygi-marshal-cleanup.h
+++ b/gi/pygi-marshal-cleanup.h
@@ -55,16 +55,6 @@ void _pygi_marshal_cleanup_to_py_interface_struct_foreign (PyGIInvokeState *stat
                                                            PyObject        *dummy,
                                                            gpointer         data,
                                                            gboolean         was_processed);
-void _pygi_marshal_cleanup_from_py_interface_object       (PyGIInvokeState *state,
-                                                           PyGIArgCache    *arg_cache,
-                                                           PyObject        *py_arg,
-                                                           gpointer         data,
-                                                           gboolean         was_processed);
-void _pygi_marshal_cleanup_to_py_interface_object         (PyGIInvokeState *state,
-                                                           PyGIArgCache    *arg_cache,
-                                                           PyObject        *dummy,
-                                                           gpointer         data,
-                                                           gboolean         was_processed);
 G_END_DECLS
 
 #endif /* __PYGI_MARSHAL_CLEANUP_H__ */
diff --git a/gi/pygi-marshal-from-py.c b/gi/pygi-marshal-from-py.c
index 92c89c8..ba79859 100644
--- a/gi/pygi-marshal-from-py.c
+++ b/gi/pygi-marshal-from-py.c
@@ -299,40 +299,6 @@ _pygi_marshal_from_py_interface_boxed (PyGIInvokeState   *state,
 }
 
 gboolean
-_pygi_marshal_from_py_interface_object (PyGIInvokeState   *state,
-                                        PyGICallableCache *callable_cache,
-                                        PyGIArgCache      *arg_cache,
-                                        PyObject          *py_arg,
-                                        GIArgument        *arg,
-                                        gpointer          *cleanup_data)
-{
-    gboolean res = FALSE;
-
-    if (py_arg == Py_None) {
-        arg->v_pointer = NULL;
-        return TRUE;
-    }
-
-    if (!PyObject_IsInstance (py_arg, ( (PyGIInterfaceCache *)arg_cache)->py_type)) {
-        PyObject *module = PyObject_GetAttrString(py_arg, "__module__");
-
-        PyErr_Format (PyExc_TypeError, "argument %s: Expected %s, but got %s%s%s",
-                      arg_cache->arg_name ? arg_cache->arg_name : "self",
-                      ( (PyGIInterfaceCache *)arg_cache)->type_name,
-                      module ? PYGLIB_PyUnicode_AsString(module) : "",
-                      module ? "." : "",
-                      py_arg->ob_type->tp_name);
-        if (module)
-            Py_DECREF (module);
-        return FALSE;
-    }
-
-    res = _pygi_marshal_from_py_gobject (py_arg, arg, arg_cache->transfer);
-    *cleanup_data = arg->v_pointer;
-    return res;
-}
-
-gboolean
 _pygi_marshal_from_py_interface_union (PyGIInvokeState   *state,
                                        PyGICallableCache *callable_cache,
                                        PyGIArgCache      *arg_cache,
@@ -345,95 +311,6 @@ _pygi_marshal_from_py_interface_union (PyGIInvokeState   *state,
     return FALSE;
 }
 
-/* _pygi_marshal_from_py_gobject:
- * py_arg: (in):
- * arg: (out):
- */
-gboolean
-_pygi_marshal_from_py_gobject (PyObject *py_arg, /*in*/
-                               GIArgument *arg,  /*out*/
-                               GITransfer transfer) {
-    GObject *gobj;
-
-    if (py_arg == Py_None) {
-        arg->v_pointer = NULL;
-        return TRUE;
-    }
-
-    if (!pygobject_check (py_arg, &PyGObject_Type)) {
-        PyObject *repr = PyObject_Repr (py_arg);
-        PyErr_Format(PyExc_TypeError, "expected GObject but got %s",
-                     PYGLIB_PyUnicode_AsString (repr));
-        Py_DECREF (repr);
-        return FALSE;
-    }
-
-    gobj = pygobject_get (py_arg);
-    if (transfer == GI_TRANSFER_EVERYTHING) {
-        /* For transfer everything, add a new ref that the callee will take ownership of.
-         * Pythons existing ref to the GObject will be managed with the PyGObject wrapper.
-         */
-        g_object_ref (gobj);
-    }
-
-    arg->v_pointer = gobj;
-    return TRUE;
-}
-
-/* _pygi_marshal_from_py_gobject_out_arg:
- * py_arg: (in):
- * arg: (out):
- *
- * A specialization for marshaling Python GObjects used for out/return values
- * from a Python implemented vfuncs, signals, or an assignment to a GObject property.
- */
-gboolean
-_pygi_marshal_from_py_gobject_out_arg (PyObject *py_arg, /*in*/
-                                       GIArgument *arg,  /*out*/
-                                       GITransfer transfer) {
-    GObject *gobj;
-    if (!_pygi_marshal_from_py_gobject (py_arg, arg, transfer)) {
-        return FALSE;
-    }
-
-    /* HACK: At this point the basic marshaling of the GObject was successful
-     * but we add some special case hacks for vfunc returns due to buggy APIs:
-     * https://bugzilla.gnome.org/show_bug.cgi?id=693393
-     */
-    gobj = arg->v_pointer;
-    if (py_arg->ob_refcnt == 1 && gobj->ref_count == 1) {
-        /* If both object ref counts are only 1 at this point (the reference held
-         * in a return tuple), we assume the GObject will be free'd before reaching
-         * its target and become invalid. So instead of getting invalid object errors
-         * we add a new GObject ref.
-         */
-        g_object_ref (gobj);
-
-        if (((PyGObject *)py_arg)->private_flags.flags & PYGOBJECT_GOBJECT_WAS_FLOATING) {
-            /*
-             * We want to re-float instances that were floating and the Python
-             * wrapper assumed ownership. With the additional caveat that there
-             * are not any strong references beyond the return tuple.
-             */
-            g_object_force_floating (gobj);
-
-        } else {
-            PyObject *repr = PyObject_Repr (py_arg);
-            gchar *msg = g_strdup_printf ("Expecting to marshal a borrowed reference for %s, "
-                                          "but nothing in Python is holding a reference to this object. "
-                                          "See: https://bugzilla.gnome.org/show_bug.cgi?id=687522";,
-                                          PYGLIB_PyUnicode_AsString(repr));
-            Py_DECREF (repr);
-            if (PyErr_WarnEx (PyExc_RuntimeWarning, msg, 2)) {
-                g_free (msg);
-                return FALSE;
-            }
-            g_free (msg);
-        }
-    }
-
-    return TRUE;
-}
 
 /* _pygi_marshal_from_py_gvalue:
  * py_arg: (in):
diff --git a/gi/pygi-marshal-from-py.h b/gi/pygi-marshal-from-py.h
index 0172c19..4b59601 100644
--- a/gi/pygi-marshal-from-py.h
+++ b/gi/pygi-marshal-from-py.h
@@ -57,12 +57,6 @@ gboolean _pygi_marshal_from_py_interface_boxed    (PyGIInvokeState   *state,
                                                    PyObject          *py_arg,
                                                    GIArgument        *arg,
                                                    gpointer          *cleanup_data);
-gboolean _pygi_marshal_from_py_interface_object   (PyGIInvokeState   *state,
-                                                   PyGICallableCache *callable_cache,
-                                                   PyGIArgCache      *arg_cache,
-                                                   PyObject          *py_arg,
-                                                   GIArgument        *arg,
-                                                   gpointer          *cleanup_data);
 gboolean _pygi_marshal_from_py_interface_union    (PyGIInvokeState   *state,
                                                    PyGICallableCache *callable_cache,
                                                    PyGIArgCache      *arg_cache,
@@ -71,12 +65,6 @@ gboolean _pygi_marshal_from_py_interface_union    (PyGIInvokeState   *state,
                                                    gpointer          *cleanup_data);
 
 /* Simplified marshalers shared between vfunc/closure and direct function calls. */
-gboolean _pygi_marshal_from_py_gobject (PyObject *py_arg, /*in*/
-                                        GIArgument *arg,  /*out*/
-                                        GITransfer transfer);
-gboolean _pygi_marshal_from_py_gobject_out_arg (PyObject *py_arg, /*in*/
-                                                GIArgument *arg,  /*out*/
-                                                GITransfer transfer);
 
 gboolean _pygi_marshal_from_py_gvalue (PyObject *py_arg, /*in*/
                                        GIArgument *arg,  /*out*/
diff --git a/gi/pygi-marshal-to-py.c b/gi/pygi-marshal-to-py.c
index 69a7c1a..536193c 100644
--- a/gi/pygi-marshal-to-py.c
+++ b/gi/pygi-marshal-to-py.c
@@ -194,15 +194,6 @@ _pygi_marshal_to_py_interface_boxed (PyGIInvokeState   *state,
 }
 
 PyObject *
-_pygi_marshal_to_py_interface_object_cache_adapter (PyGIInvokeState   *state,
-                                                    PyGICallableCache *callable_cache,
-                                                    PyGIArgCache      *arg_cache,
-                                                    GIArgument        *arg)
-{
-    return _pygi_marshal_to_py_object(arg, arg_cache->transfer);
-}
-
-PyObject *
 _pygi_marshal_to_py_interface_union  (PyGIInvokeState   *state,
                                       PyGICallableCache *callable_cache,
                                       PyGIArgCache      *arg_cache,
@@ -216,28 +207,6 @@ _pygi_marshal_to_py_interface_union  (PyGIInvokeState   *state,
 }
 
 PyObject *
-_pygi_marshal_to_py_object (GIArgument *arg, GITransfer transfer) {
-    PyObject *pyobj;
-
-    if (arg->v_pointer == NULL) {
-        pyobj = Py_None;
-        Py_INCREF (pyobj);
-
-    } else if (G_IS_PARAM_SPEC(arg->v_pointer)) {
-        pyobj = pyg_param_spec_new (arg->v_pointer);
-        if (transfer == GI_TRANSFER_EVERYTHING)
-            g_param_spec_unref (arg->v_pointer);
-
-    } else {
-         pyobj = pygobject_new_full (arg->v_pointer,
-                                     /*steal=*/ transfer == GI_TRANSFER_EVERYTHING,
-                                     /*type=*/  NULL);
-    }
-
-    return pyobj;
-}
-
-PyObject *
 _pygi_marshal_to_py_interface_struct (GIArgument *arg,
                                       GIInterfaceInfo *interface_info,
                                       GType g_type,
diff --git a/gi/pygi-marshal-to-py.h b/gi/pygi-marshal-to-py.h
index de8f85b..aa84503 100644
--- a/gi/pygi-marshal-to-py.h
+++ b/gi/pygi-marshal-to-py.h
@@ -42,20 +42,12 @@ PyObject *_pygi_marshal_to_py_interface_boxed  (PyGIInvokeState   *state,
                                                 PyGICallableCache *callable_cache,
                                                 PyGIArgCache      *arg_cache,
                                                 GIArgument        *arg);
-PyObject *_pygi_marshal_to_py_interface_object_cache_adapter (PyGIInvokeState   *state,
-                                                              PyGICallableCache *callable_cache,
-                                                              PyGIArgCache      *arg_cache,
-                                                              GIArgument        *arg);
 PyObject *_pygi_marshal_to_py_interface_union  (PyGIInvokeState   *state,
                                                 PyGICallableCache *callable_cache,
                                                 PyGIArgCache      *arg_cache,
                                                 GIArgument        *arg);
 
 /* Simplified marshalers shared between vfunc/closure and direct function calls. */
-
-PyObject *_pygi_marshal_to_py_object (GIArgument *arg,
-                                      GITransfer transfer);
-
 PyObject *_pygi_marshal_to_py_interface_struct (GIArgument *arg,
                                                 GIInterfaceInfo *interface_info,
                                                 GType g_type,
diff --git a/gi/pygi-object.c b/gi/pygi-object.c
new file mode 100644
index 0000000..4ad58b1
--- /dev/null
+++ b/gi/pygi-object.c
@@ -0,0 +1,273 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * vim: tabstop=4 shiftwidth=4 expandtab
+ *
+ * Copyright (C) 2011 John (J5) Palmieri <johnp redhat com>
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <Python.h>
+#include <pyglib-python-compat.h>
+
+#include "pygi-arg-gobject.h"
+#include "pygi-private.h"
+#include "pygparamspec.h"
+
+/*
+ * GObject from Python
+ */
+
+/* _pygi_marshal_from_py_gobject:
+ * py_arg: (in):
+ * arg: (out):
+ */
+static gboolean
+_pygi_marshal_from_py_gobject (PyObject *py_arg, /*in*/
+                               GIArgument *arg,  /*out*/
+                               GITransfer transfer) {
+    GObject *gobj;
+
+    if (py_arg == Py_None) {
+        arg->v_pointer = NULL;
+        return TRUE;
+    }
+
+    if (!pygobject_check (py_arg, &PyGObject_Type)) {
+        PyObject *repr = PyObject_Repr (py_arg);
+        PyErr_Format(PyExc_TypeError, "expected GObject but got %s",
+                     PYGLIB_PyUnicode_AsString (repr));
+        Py_DECREF (repr);
+        return FALSE;
+    }
+
+    gobj = pygobject_get (py_arg);
+    if (transfer == GI_TRANSFER_EVERYTHING) {
+        /* For transfer everything, add a new ref that the callee will take ownership of.
+         * Pythons existing ref to the GObject will be managed with the PyGObject wrapper.
+         */
+        g_object_ref (gobj);
+    }
+
+    arg->v_pointer = gobj;
+    return TRUE;
+}
+
+/* pygi_arg_gobject_out_arg_from_py:
+ * py_arg: (in):
+ * arg: (out):
+ *
+ * A specialization for marshaling Python GObjects used for out/return values
+ * from a Python implemented vfuncs, signals, or an assignment to a GObject property.
+ */
+gboolean
+pygi_arg_gobject_out_arg_from_py (PyObject *py_arg, /*in*/
+                                  GIArgument *arg,  /*out*/
+                                  GITransfer transfer) {
+    GObject *gobj;
+    if (!_pygi_marshal_from_py_gobject (py_arg, arg, transfer)) {
+        return FALSE;
+    }
+
+    /* HACK: At this point the basic marshaling of the GObject was successful
+     * but we add some special case hacks for vfunc returns due to buggy APIs:
+     * https://bugzilla.gnome.org/show_bug.cgi?id=693393
+     */
+    gobj = arg->v_pointer;
+    if (py_arg->ob_refcnt == 1 && gobj->ref_count == 1) {
+        /* If both object ref counts are only 1 at this point (the reference held
+         * in a return tuple), we assume the GObject will be free'd before reaching
+         * its target and become invalid. So instead of getting invalid object errors
+         * we add a new GObject ref.
+         */
+        g_object_ref (gobj);
+
+        if (((PyGObject *)py_arg)->private_flags.flags & PYGOBJECT_GOBJECT_WAS_FLOATING) {
+            /*
+             * We want to re-float instances that were floating and the Python
+             * wrapper assumed ownership. With the additional caveat that there
+             * are not any strong references beyond the return tuple.
+             */
+            g_object_force_floating (gobj);
+
+        } else {
+            PyObject *repr = PyObject_Repr (py_arg);
+            gchar *msg = g_strdup_printf ("Expecting to marshal a borrowed reference for %s, "
+                                          "but nothing in Python is holding a reference to this object. "
+                                          "See: https://bugzilla.gnome.org/show_bug.cgi?id=687522";,
+                                          PYGLIB_PyUnicode_AsString(repr));
+            Py_DECREF (repr);
+            if (PyErr_WarnEx (PyExc_RuntimeWarning, msg, 2)) {
+                g_free (msg);
+                return FALSE;
+            }
+            g_free (msg);
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean
+_pygi_marshal_from_py_interface_object (PyGIInvokeState   *state,
+                                        PyGICallableCache *callable_cache,
+                                        PyGIArgCache      *arg_cache,
+                                        PyObject          *py_arg,
+                                        GIArgument        *arg,
+                                        gpointer          *cleanup_data)
+{
+    gboolean res = FALSE;
+
+    if (py_arg == Py_None) {
+        arg->v_pointer = NULL;
+        return TRUE;
+    }
+
+    if (!PyObject_IsInstance (py_arg, ( (PyGIInterfaceCache *)arg_cache)->py_type)) {
+        PyObject *module = PyObject_GetAttrString(py_arg, "__module__");
+
+        PyErr_Format (PyExc_TypeError, "argument %s: Expected %s, but got %s%s%s",
+                      arg_cache->arg_name ? arg_cache->arg_name : "self",
+                      ( (PyGIInterfaceCache *)arg_cache)->type_name,
+                      module ? PYGLIB_PyUnicode_AsString(module) : "",
+                      module ? "." : "",
+                      py_arg->ob_type->tp_name);
+        if (module)
+            Py_DECREF (module);
+        return FALSE;
+    }
+
+    res = _pygi_marshal_from_py_gobject (py_arg, arg, arg_cache->transfer);
+    *cleanup_data = arg->v_pointer;
+    return res;
+}
+
+static void
+_pygi_marshal_cleanup_from_py_interface_object (PyGIInvokeState *state,
+                                                PyGIArgCache    *arg_cache,
+                                                PyObject        *py_arg,
+                                                gpointer         data,
+                                                gboolean         was_processed)
+{
+    /* If we processed the parameter but fail before invoking the method,
+       we need to remove the ref we added */
+    if (was_processed && state->failed && data != NULL &&
+            arg_cache->transfer == GI_TRANSFER_EVERYTHING)
+        g_object_unref (G_OBJECT(data));
+}
+
+
+/*
+ * GObject to Python
+ */
+
+PyObject *
+pygi_arg_gobject_to_py (GIArgument *arg, GITransfer transfer) {
+    PyObject *pyobj;
+
+    if (arg->v_pointer == NULL) {
+        pyobj = Py_None;
+        Py_INCREF (pyobj);
+
+    } else if (G_IS_PARAM_SPEC(arg->v_pointer)) {
+        pyobj = pyg_param_spec_new (arg->v_pointer);
+        if (transfer == GI_TRANSFER_EVERYTHING)
+            g_param_spec_unref (arg->v_pointer);
+
+    } else {
+         pyobj = pygobject_new_full (arg->v_pointer,
+                                     /*steal=*/ transfer == GI_TRANSFER_EVERYTHING,
+                                     /*type=*/  NULL);
+    }
+
+    return pyobj;
+}
+
+static PyObject *
+_pygi_marshal_to_py_interface_object_cache_adapter (PyGIInvokeState   *state,
+                                                    PyGICallableCache *callable_cache,
+                                                    PyGIArgCache      *arg_cache,
+                                                    GIArgument        *arg)
+{
+    return pygi_arg_gobject_to_py(arg, arg_cache->transfer);
+}
+
+static void
+_pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state,
+                                              PyGIArgCache    *arg_cache,
+                                              PyObject        *dummy,
+                                              gpointer         data,
+                                              gboolean         was_processed)
+{
+    /* If we error out and the object is not marshalled into a PyGObject
+       we must take care of removing the ref */
+    if (!was_processed && arg_cache->transfer == GI_TRANSFER_EVERYTHING)
+        g_object_unref (G_OBJECT(data));
+}
+
+static gboolean
+pygi_arg_gobject_setup_from_info (PyGIArgCache  *arg_cache,
+                                  GITypeInfo    *type_info,
+                                  GIArgInfo     *arg_info,
+                                  GITransfer     transfer,
+                                  PyGIDirection  direction)
+{
+    /* NOTE: usage of pygi_arg_interface_new_from_info already calls
+     * pygi_arg_interface_setup so no need to do it here.
+     */
+
+    if (direction & PYGI_DIRECTION_FROM_PYTHON) {
+        arg_cache->from_py_marshaller = _pygi_marshal_from_py_interface_object;
+        arg_cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_object;
+    }
+
+    if (direction & PYGI_DIRECTION_TO_PYTHON) {
+        arg_cache->to_py_marshaller = _pygi_marshal_to_py_interface_object_cache_adapter;
+        arg_cache->to_py_cleanup = _pygi_marshal_cleanup_to_py_interface_object;
+    }
+
+    return TRUE;
+}
+
+PyGIArgCache *
+pygi_arg_gobject_new_from_info (GITypeInfo      *type_info,
+                                GIArgInfo       *arg_info,
+                                GITransfer       transfer,
+                                PyGIDirection    direction,
+                                GIInterfaceInfo *iface_info)
+{
+    gboolean res = FALSE;
+    PyGIArgCache *cache = NULL;
+
+    cache = pygi_arg_interface_new_from_info (type_info,
+                                              arg_info,
+                                              transfer,
+                                              direction,
+                                              iface_info);
+    if (cache == NULL)
+        return NULL;
+
+    res = pygi_arg_gobject_setup_from_info (cache,
+                                            type_info,
+                                            arg_info,
+                                            transfer,
+                                            direction);
+    if (res) {
+        return cache;
+    } else {
+        _pygi_arg_cache_free (cache);
+        return NULL;
+    }
+}
diff --git a/gi/pygi-object.h b/gi/pygi-object.h
new file mode 100644
index 0000000..4a8800e
--- /dev/null
+++ b/gi/pygi-object.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; c-basic-offset: 4 -*-
+ * vim: tabstop=4 shiftwidth=4 expandtab
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PYGI_OBJECT_H__
+#define __PYGI_OBJECT_H__
+
+#include <girepository.h>
+#include "pygi-cache.h"
+
+G_BEGIN_DECLS
+
+gboolean
+pygi_arg_gobject_out_arg_from_py     (PyObject          *py_arg,     /* in */
+                                      GIArgument        *arg,        /* out */
+                                      GITransfer         transfer);
+
+PyObject *
+pygi_arg_gobject_to_py               (GIArgument        *arg,
+                                      GITransfer         transfer);
+
+PyGIArgCache *
+pygi_arg_gobject_new_from_info       (GITypeInfo        *type_info,
+                                      GIArgInfo         *arg_info,   /* may be null */
+                                      GITransfer         transfer,
+                                      PyGIDirection      direction,
+                                      GIInterfaceInfo   *iface_info);
+
+G_END_DECLS
+
+#endif /*__PYGI_OBJECT_H__*/



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