summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Berg <bberg@redhat.com>2020-11-13 00:21:49 +0100
committerBenjamin Berg <benjamin@sipsolutions.net>2022-12-26 15:37:36 +0100
commit5e6bc1023e6c8553534b04a34495e9470b06c11f (patch)
treed8e2382aa7586566491a4d61508d89a61b06d714
parent3c3fa39e7f9c0fd604ca70c6d0bdd5dbf5bb5354 (diff)
downloadpygobject-5e6bc1023e6c8553534b04a34495e9470b06c11f.tar.gz
cache: Resolve and store information about async functions
This also resolves information about async functions in a generic and safe way. The corresponding _finish function will be stored for later reference and an attribute is added to cache the return object type for an async function.
-rw-r--r--gi/pygi-cache.c79
-rw-r--r--gi/pygi-cache.h11
-rw-r--r--gi/pygi-closure.c3
3 files changed, 93 insertions, 0 deletions
diff --git a/gi/pygi-cache.c b/gi/pygi-cache.c
index c6630de0..500b648b 100644
--- a/gi/pygi-cache.c
+++ b/gi/pygi-cache.c
@@ -172,6 +172,11 @@ pygi_arg_interface_setup (PyGIInterfaceCache *iface_cache,
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 (g_type_is_a (iface_cache->g_type, G_TYPE_OBJECT)) {
+ if (g_str_equal (g_type_name (iface_cache->g_type), "GCancellable"))
+ iface_cache->arg_cache.async_context = PYGI_ASYNC_CONTEXT_CANCELLABLE;
+ }
+
if (iface_cache->py_type == NULL) {
return FALSE;
}
@@ -787,8 +792,11 @@ _function_cache_invoke_real (PyGIFunctionCache *function_cache,
static void
_function_cache_deinit_real (PyGICallableCache *callable_cache)
{
+ PyGIFunctionCache *function_cache = (PyGIFunctionCache *) callable_cache;
g_function_invoker_destroy (&((PyGIFunctionCache *) callable_cache)->invoker);
+ Py_CLEAR (function_cache->async_finish);
+
_callable_cache_deinit_real (callable_cache);
}
@@ -799,6 +807,7 @@ _function_cache_init (PyGIFunctionCache *function_cache,
PyGICallableCache *callable_cache = (PyGICallableCache *) function_cache;
GIFunctionInvoker *invoker = &function_cache->invoker;
GError *error = NULL;
+ guint i;
callable_cache->calling_context = PYGI_CALLING_CONTEXT_IS_FROM_PY;
@@ -811,6 +820,76 @@ _function_cache_init (PyGIFunctionCache *function_cache,
if (!_callable_cache_init (callable_cache, callable_info))
return FALSE;
+ /* Check if this function is an async routine that is capable of returning
+ * an async awaitable object.
+ */
+ if (!callable_cache->has_return && callable_cache->n_to_py_args == 0) {
+ PyGIArgCache *cancellable = NULL;
+ PyGIArgCache *async_callback = NULL;
+
+ for (i = 0; i < _pygi_callable_cache_args_len (callable_cache); i++) {
+ PyGIArgCache *arg_cache = _pygi_callable_cache_get_arg (callable_cache, i);
+
+ /* Ignore any out or in/out parameters. */
+ if (arg_cache->async_context == PYGI_ASYNC_CONTEXT_CALLBACK) {
+ if (async_callback) {
+ async_callback = NULL;
+ break;
+ }
+ async_callback = arg_cache;
+ } else if (arg_cache->async_context == PYGI_ASYNC_CONTEXT_CANCELLABLE) {
+ if (cancellable) {
+ cancellable = NULL;
+ break;
+ }
+ cancellable = arg_cache;
+ }
+ }
+
+ if (cancellable && async_callback) {
+ GIBaseInfo *container = g_base_info_get_container ((GIBaseInfo*) callable_info);
+ GIBaseInfo *async_finish = NULL;
+ gint name_len;
+ gchar *finish_name = NULL;
+
+ /* This appears to be an async routine. As we have the
+ * GCallableInfo at this point, so guess the finish name and look
+ * up that information.
+ */
+ name_len = strlen (callable_cache->name);
+ if (g_str_has_suffix (callable_cache->name, "_async"))
+ name_len -= 6;
+
+ /* Original name without _async if it is there, _finish + NUL byte */
+ finish_name = g_malloc0 (name_len + 7 + 1);
+ strncat (finish_name, callable_cache->name, name_len);
+ strcat (finish_name, "_finish");
+
+ if (container && g_base_info_get_type (container) == GI_INFO_TYPE_OBJECT) {
+ async_finish = g_object_info_find_method ((GIObjectInfo *) container, finish_name);
+ } else if (container && g_base_info_get_type (container) == GI_INFO_TYPE_INTERFACE) {
+ async_finish = g_interface_info_find_method ((GIInterfaceInfo *) container, finish_name);
+ } else if (!container) {
+ async_finish = g_irepository_find_by_name (NULL,
+ callable_cache->namespace,
+ finish_name);
+ } else {
+ g_debug ("Awaitable async functions only work on GObjects and as toplevel functions.");
+ }
+
+ if (async_finish && g_base_info_get_type (async_finish)) {
+ function_cache->async_finish = _pygi_info_new ((GIBaseInfo *) async_finish);
+ function_cache->async_cancellable = cancellable;
+ function_cache->async_callback = async_callback;
+ }
+
+ if (async_finish)
+ g_base_info_unref (async_finish);
+
+ g_free (finish_name);
+ }
+ }
+
/* Set by PyGICCallbackCache and PyGIVFuncCache */
if (invoker->native_address == NULL) {
if (g_function_info_prep_invoker ((GIFunctionInfo *) callable_info,
diff --git a/gi/pygi-cache.h b/gi/pygi-cache.h
index fc5a6162..252c96ad 100644
--- a/gi/pygi-cache.h
+++ b/gi/pygi-cache.h
@@ -104,12 +104,18 @@ typedef enum {
PYGI_CALLING_CONTEXT_IS_FROM_PY
} PyGICallingContext;
+typedef enum {
+ PYGI_ASYNC_CONTEXT_NONE = 0,
+ PYGI_ASYNC_CONTEXT_CALLBACK,
+ PYGI_ASYNC_CONTEXT_CANCELLABLE,
+} PyGIAsyncContext;
struct _PyGIArgCache
{
const gchar *arg_name;
PyGIMetaArgType meta_type;
+ PyGIAsyncContext async_context;
gboolean is_pointer;
gboolean is_caller_allocates;
gboolean is_skipped;
@@ -218,6 +224,11 @@ struct _PyGICallableCache
struct _PyGIFunctionCache {
PyGICallableCache callable_cache;
+ /* Information about async functions. */
+ PyObject *async_finish;
+ PyGIArgCache *async_callback;
+ PyGIArgCache *async_cancellable;
+
/* An invoker with ffi_cif already setup */
GIFunctionInvoker invoker;
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
index 919365a3..6b3889c7 100644
--- a/gi/pygi-closure.c
+++ b/gi/pygi-closure.c
@@ -917,6 +917,9 @@ pygi_arg_callback_setup_from_info (PyGICallbackCache *arg_cache,
arg_cache->closure_cache = pygi_closure_cache_new (arg_cache->interface_info);
cache->from_py_marshaller = _pygi_marshal_from_py_interface_callback;
cache->from_py_cleanup = _pygi_marshal_cleanup_from_py_interface_callback;
+
+ if (arg_cache->scope == GI_SCOPE_TYPE_ASYNC)
+ arg_cache->arg_cache.async_context = PYGI_ASYNC_CONTEXT_CALLBACK;
}
if (direction & PYGI_DIRECTION_TO_PYTHON) {