예제 #1
0
static gboolean
_pygi_marshal_from_py_interface_callback (PyGIInvokeState   *state,
                                          PyGICallableCache *callable_cache,
                                          PyGIArgCache      *arg_cache,
                                          PyObject          *py_arg,
                                          GIArgument        *arg,
                                          gpointer          *cleanup_data)
{
    GICallableInfo *callable_info;
    PyGICClosure *closure;
    PyGIArgCache *user_data_cache = NULL;
    PyGIArgCache *destroy_cache = NULL;
    PyGICallbackCache *callback_cache;
    PyObject *py_user_data = NULL;

    callback_cache = (PyGICallbackCache *)arg_cache;

    if (callback_cache->user_data_index > 0) {
        user_data_cache = _pygi_callable_cache_get_arg (callable_cache, callback_cache->user_data_index);
        if (user_data_cache->py_arg_index < state->n_py_in_args) {
            /* py_user_data is a borrowed reference. */
            py_user_data = PyTuple_GetItem (state->py_in_args, user_data_cache->py_arg_index);
            if (!py_user_data)
                return FALSE;
            /* NULL out user_data if it was not supplied and the default arg placeholder
             * was used instead.
             */
            if (py_user_data == _PyGIDefaultArgPlaceholder) {
                py_user_data = NULL;
            } else if (callable_cache->user_data_varargs_index < 0) {
                /* For non-variable length user data, place the user data in a
                 * single item tuple which is concatenated to the callbacks arguments.
                 * This allows callback input arg marshaling to always expect a
                 * tuple for user data. Note the
                 */
                py_user_data = Py_BuildValue("(O)", py_user_data, NULL);
            } else {
                /* increment the ref borrowed from PyTuple_GetItem above */
                Py_INCREF (py_user_data);
            }
        }
    }

    if (py_arg == Py_None) {
        return TRUE;
    }

    if (!PyCallable_Check (py_arg)) {
        PyErr_Format (PyExc_TypeError,
                      "Callback needs to be a function or method not %s",
                      py_arg->ob_type->tp_name);

        return FALSE;
    }

    callable_info = (GICallableInfo *)callback_cache->interface_info;

    closure = _pygi_make_native_closure (callable_info, callback_cache->scope, py_arg, py_user_data);
    arg->v_pointer = closure->closure;

    /* always decref the user data as _pygi_make_native_closure adds its own ref */
    Py_XDECREF (py_user_data);

    /* The PyGICClosure instance is used as user data passed into the C function.
     * The return trip to python will marshal this back and pull the python user data out.
     */
    if (user_data_cache != NULL) {
        state->args[user_data_cache->c_arg_index].arg_value.v_pointer = closure;
    }

    /* Setup a GDestroyNotify callback if this method supports it along with
     * a user data field. The user data field is a requirement in order
     * free resources and ref counts associated with this arguments closure.
     * In case a user data field is not available, show a warning giving
     * explicit information and setup a dummy notification to avoid a crash
     * later on in _pygi_destroy_notify_callback_closure.
     */
    if (callback_cache->destroy_notify_index > 0) {
        destroy_cache = _pygi_callable_cache_get_arg (callable_cache, callback_cache->destroy_notify_index);
    }

    if (destroy_cache) {
        if (user_data_cache != NULL) {
            state->args[destroy_cache->c_arg_index].arg_value.v_pointer = _pygi_invoke_closure_free;
        } else {
            char *full_name = pygi_callable_cache_get_full_name (callable_cache);
            gchar *msg = g_strdup_printf("Callables passed to %s will leak references because "
                                         "the method does not support a user_data argument. "
                                         "See: https://bugzilla.gnome.org/show_bug.cgi?id=685598",
                                         full_name);
            g_free (full_name);
            if (PyErr_WarnEx(PyExc_RuntimeWarning, msg, 2)) {
                g_free(msg);
                _pygi_invoke_closure_free(closure);
                return FALSE;
            }
            g_free(msg);
            state->args[destroy_cache->c_arg_index].arg_value.v_pointer = _pygi_destroy_notify_dummy;
        }
    }

    /* Use the PyGIClosure as data passed to cleanup for GI_SCOPE_TYPE_CALL. */
    *cleanup_data = closure;

    return TRUE;
}
예제 #2
0
gboolean
_pygi_create_callback (GIBaseInfo  *function_info,
                       gboolean       is_method,
                       gboolean       is_constructor,
                       int            n_args,
                       Py_ssize_t     py_argc,
                       PyObject      *py_argv,
                       guint8         callback_index,
                       guint8         user_data_index,
                       guint8         destroy_notify_index,
                       PyGICClosure **closure_out)
{
    GIArgInfo *callback_arg;
    GITypeInfo *callback_type;
    GICallbackInfo *callback_info;
    GIScopeType scope;
    gboolean found_py_function;
    PyObject *py_function;
    guint8 i, py_argv_pos;
    PyObject *py_user_data;
    gboolean allow_none;

    callback_arg = g_callable_info_get_arg ( (GICallableInfo*) function_info, callback_index);
    scope = g_arg_info_get_scope (callback_arg);
    allow_none = g_arg_info_may_be_null (callback_arg);

    callback_type = g_arg_info_get_type (callback_arg);
    g_assert (g_type_info_get_tag (callback_type) == GI_TYPE_TAG_INTERFACE);

    callback_info = (GICallbackInfo*) g_type_info_get_interface (callback_type);
    g_assert (g_base_info_get_type ( (GIBaseInfo*) callback_info) == GI_INFO_TYPE_CALLBACK);

    /* Find the Python function passed for the callback */
    found_py_function = FALSE;
    py_function = Py_None;
    py_user_data = NULL;

    /* if its a method then we need to skip over 'self' */
    if (is_method || is_constructor)
        py_argv_pos = 1;
    else
        py_argv_pos = 0;

    for (i = 0; i < n_args && i < py_argc; i++) {
        if (i == callback_index) {
            py_function = PyTuple_GetItem (py_argv, py_argv_pos);
            /* if we allow none then set the closure to NULL and return */
            if (allow_none && py_function == Py_None) {
                *closure_out = NULL;
                goto out;
            }
            found_py_function = TRUE;
        } else if (i == user_data_index) {
            py_user_data = PyTuple_GetItem (py_argv, py_argv_pos);
        }
        py_argv_pos++;
    }

    if (!found_py_function
            || (py_function == Py_None || !PyCallable_Check (py_function))) {
        PyErr_Format (PyExc_TypeError, "Error invoking %s.%s: Unexpected value "
                      "for argument '%s'",
                      g_base_info_get_namespace ( (GIBaseInfo*) function_info),
                      g_base_info_get_name ( (GIBaseInfo*) function_info),
                      g_base_info_get_name ( (GIBaseInfo*) callback_arg));
        g_base_info_unref ( (GIBaseInfo*) callback_info);
        g_base_info_unref ( (GIBaseInfo*) callback_type);
        return FALSE;
    }

    /** Now actually build the closure **/
    *closure_out = _pygi_make_native_closure ( (GICallableInfo *) callback_info,
                                               g_arg_info_get_scope (callback_arg),
                                               py_function,
                                               py_user_data);
out:
    g_base_info_unref ( (GIBaseInfo*) callback_info);
    g_base_info_unref ( (GIBaseInfo*) callback_type);

    return TRUE;
}