diff options
author | Benjamin Berg <bberg@redhat.com> | 2020-11-13 00:21:49 +0100 |
---|---|---|
committer | Benjamin Berg <benjamin@sipsolutions.net> | 2022-12-26 15:37:36 +0100 |
commit | 5e6bc1023e6c8553534b04a34495e9470b06c11f (patch) | |
tree | d8e2382aa7586566491a4d61508d89a61b06d714 | |
parent | 3c3fa39e7f9c0fd604ca70c6d0bdd5dbf5bb5354 (diff) | |
download | pygobject-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.c | 79 | ||||
-rw-r--r-- | gi/pygi-cache.h | 11 | ||||
-rw-r--r-- | gi/pygi-closure.c | 3 |
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) { |