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; }
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; }