summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Berg <bberg@redhat.com>2020-11-13 00:26:53 +0100
committerBenjamin Berg <bberg@redhat.com>2021-07-13 01:06:43 +0200
commit7edb9032bb0898e921559e43630c8ea8b9ac14ac (patch)
treed6cb74e50140e4b6235a6a5722f65ae16d724b8a
parent8883c69b1afc66e03e311a2fa61d49271cae3950 (diff)
downloadpygobject-wip/gio-async-awaitable-return.tar.gz
invoke: Create async helper when needed and marshal it for callbackswip/gio-async-awaitable-return
Note that a cancellable will implicitly be created so that the awaitable can be cancelled. If a cancellable is given, then this cancellable will be used instead.
-rw-r--r--gi/pygi-invoke.c51
1 files changed, 48 insertions, 3 deletions
diff --git a/gi/pygi-invoke.c b/gi/pygi-invoke.c
index 4239f255..e4570469 100644
--- a/gi/pygi-invoke.c
+++ b/gi/pygi-invoke.c
@@ -409,10 +409,28 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGIFunctionCache *function_cac
return FALSE;
}
+ if (function_cache->async_finish && function_cache->async_callback &&
+ function_cache->async_callback->py_arg_index < state->n_py_in_args &&
+ PyTuple_GET_ITEM (state->py_in_args, function_cache->async_callback->py_arg_index) == _PyGIDefaultArgPlaceholder) {
+
+ /* We are dealing with an async call that returns an awaitable */
+ PyObject *cancellable = NULL;
+
+ /* Try to resolve any passed GCancellable. */
+ if (function_cache->async_cancellable && function_cache->async_cancellable->py_arg_index < state->n_py_in_args)
+ cancellable = PyTuple_GET_ITEM (state->py_in_args, function_cache->async_cancellable->py_arg_index);
+
+ if (cancellable == _PyGIDefaultArgPlaceholder)
+ cancellable = NULL;
+
+ state->py_async = pygi_async_new (function_cache->async_finish, cancellable);
+ }
+
for (i = 0; (gsize)i < _pygi_callable_cache_args_len (cache); i++) {
GIArgument *c_arg = &state->args[i].arg_value;
PyGIArgCache *arg_cache = g_ptr_array_index (cache->args_cache, i);
PyObject *py_arg = NULL;
+ gboolean marshal = TRUE;
switch (arg_cache->direction) {
case PYGI_DIRECTION_FROM_PYTHON:
@@ -512,9 +530,25 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGIFunctionCache *function_cac
}
if (py_arg == _PyGIDefaultArgPlaceholder) {
- *c_arg = arg_cache->default_value;
- } else if (arg_cache->from_py_marshaller != NULL &&
- arg_cache->meta_type != PYGI_META_ARG_TYPE_CHILD) {
+ /* If this is the cancellable, then we may override it later if we
+ * detect an async call.
+ */
+ marshal = FALSE;
+
+ if (state->py_async && arg_cache->async_context == PYGI_ASYNC_CONTEXT_CANCELLABLE) {
+ marshal = TRUE;
+ py_arg = ((PyGIAsync*) state->py_async)->cancellable;
+ } else if (state->py_async && arg_cache->async_context == PYGI_ASYNC_CONTEXT_CALLBACK) {
+ marshal = TRUE;
+ } else {
+ *c_arg = arg_cache->default_value;
+ }
+ }
+
+ if (marshal &&
+ arg_cache->from_py_marshaller != NULL &&
+ arg_cache->meta_type != PYGI_META_ARG_TYPE_CHILD) {
+
gboolean success;
gpointer cleanup_data = NULL;
@@ -587,6 +621,17 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGIFunctionCache *function_ca
}
}
+ /* Return the async future if we have one. */
+ if (state->py_async) {
+ /* We must have no return value */
+ g_assert (n_out_args == 0);
+ g_assert (cache->return_cache->is_skipped || cache->return_cache->type_tag == GI_TYPE_TAG_VOID);
+
+ Py_DECREF(py_return);
+ Py_INCREF(state->py_async);
+ return state->py_async;
+ }
+
if (n_out_args == 0) {
if (cache->return_cache->is_skipped && state->error == NULL) {
/* we skip the return value and have no (out) arguments to return,