static gboolean _check_for_unexpected_kwargs (PyGICallableCache *cache, GHashTable *arg_name_hash, PyObject *py_kwargs) { PyObject *dict_key, *dict_value; Py_ssize_t dict_iter_pos = 0; while (PyDict_Next (py_kwargs, &dict_iter_pos, &dict_key, &dict_value)) { PyObject *key; #if PY_VERSION_HEX < 0x03000000 if (PyString_Check (dict_key)) { Py_INCREF (dict_key); key = dict_key; } else #endif { key = PyUnicode_AsUTF8String (dict_key); if (key == NULL) { return FALSE; } } /* Use extended lookup because it returns whether or not the key actually * exists in the hash table. g_hash_table_lookup returns NULL for keys not * found which maps to index 0 for our hash lookup. */ if (!g_hash_table_lookup_extended (arg_name_hash, PyBytes_AsString(key), NULL, NULL)) { char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%.200s() got an unexpected keyword argument '%.400s'", full_name, PyBytes_AsString (key)); Py_DECREF (key); g_free (full_name); return FALSE; } Py_DECREF (key); } return TRUE; }
/** * _py_args_combine_and_check_length: * @cache: PyGICallableCache * @py_args: the tuple of positional arguments. * @py_kwargs: the dict of keyword arguments to be merged with py_args. * * Returns: New value reference to the combined py_args and py_kwargs. */ static PyObject * _py_args_combine_and_check_length (PyGICallableCache *cache, PyObject *py_args, PyObject *py_kwargs) { PyObject *combined_py_args = NULL; Py_ssize_t n_py_args, n_py_kwargs, i; guint n_expected_args = cache->n_py_args; GSList *l; n_py_args = PyTuple_GET_SIZE (py_args); if (py_kwargs == NULL) n_py_kwargs = 0; else n_py_kwargs = PyDict_Size (py_kwargs); /* Fast path, we already have the exact number of args and not kwargs. */ if (n_py_kwargs == 0 && n_py_args == n_expected_args && cache->user_data_varargs_index < 0) { Py_INCREF (py_args); return py_args; } if (cache->user_data_varargs_index < 0 && n_expected_args < n_py_args) { char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%.200s() takes exactly %d %sargument%s (%zd given)", full_name, n_expected_args, n_py_kwargs > 0 ? "non-keyword " : "", n_expected_args == 1 ? "" : "s", n_py_args); g_free (full_name); return NULL; } if (cache->user_data_varargs_index >= 0 && n_py_kwargs > 0 && n_expected_args < n_py_args) { char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%.200s() cannot use variable user data arguments with keyword arguments", full_name); g_free (full_name); return NULL; } if (n_py_kwargs > 0 && !_check_for_unexpected_kwargs (cache, cache->arg_name_hash, py_kwargs)) { return NULL; } /* will hold arguments from both py_args and py_kwargs * when they are combined into a single tuple */ combined_py_args = PyTuple_New (n_expected_args); for (i = 0, l = cache->arg_name_list; i < n_expected_args && l; i++, l = l->next) { PyObject *py_arg_item = NULL; PyObject *kw_arg_item = NULL; const gchar *arg_name = l->data; int arg_cache_index = -1; gboolean is_varargs_user_data = FALSE; if (arg_name != NULL) arg_cache_index = GPOINTER_TO_INT (g_hash_table_lookup (cache->arg_name_hash, arg_name)); is_varargs_user_data = cache->user_data_varargs_index >= 0 && arg_cache_index == cache->user_data_varargs_index; if (n_py_kwargs > 0 && arg_name != NULL) { /* NULL means this argument has no keyword name */ /* ex. the first argument to a method or constructor */ kw_arg_item = PyDict_GetItemString (py_kwargs, arg_name); } /* use a bounded retrieval of the original input */ if (i < n_py_args) py_arg_item = PyTuple_GET_ITEM (py_args, i); if (kw_arg_item == NULL && py_arg_item != NULL) { if (is_varargs_user_data) { /* For tail end user_data varargs, pull a slice off and we are done. */ PyObject *user_data = PyTuple_GetSlice (py_args, i, PY_SSIZE_T_MAX); PyTuple_SET_ITEM (combined_py_args, i, user_data); return combined_py_args; } else { Py_INCREF (py_arg_item); PyTuple_SET_ITEM (combined_py_args, i, py_arg_item); } } else if (kw_arg_item != NULL && py_arg_item == NULL) { if (is_varargs_user_data) { /* Special case where user_data is passed as a keyword argument (user_data=foo) * Wrap the value in a tuple to represent variable args for marshaling later on. */ PyObject *user_data = Py_BuildValue("(O)", kw_arg_item, NULL); PyTuple_SET_ITEM (combined_py_args, i, user_data); } else { Py_INCREF (kw_arg_item); PyTuple_SET_ITEM (combined_py_args, i, kw_arg_item); } } else if (kw_arg_item == NULL && py_arg_item == NULL) { if (is_varargs_user_data) { /* For varargs user_data, pass an empty tuple when nothing is given. */ PyTuple_SET_ITEM (combined_py_args, i, PyTuple_New (0)); } else if (arg_cache_index >= 0 && _pygi_callable_cache_get_arg (cache, arg_cache_index)->has_default) { /* If the argument supports a default, use a place holder in the * argument tuple, this will be checked later during marshaling. */ Py_INCREF (_PyGIDefaultArgPlaceholder); PyTuple_SET_ITEM (combined_py_args, i, _PyGIDefaultArgPlaceholder); } else { char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%.200s() takes exactly %d %sargument%s (%zd given)", full_name, n_expected_args, n_py_kwargs > 0 ? "non-keyword " : "", n_expected_args == 1 ? "" : "s", n_py_args); g_free (full_name); Py_DECREF (combined_py_args); return NULL; } } else if (kw_arg_item != NULL && py_arg_item != NULL) { char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%.200s() got multiple values for keyword argument '%.200s'", full_name, arg_name); Py_DECREF (combined_py_args); g_free (full_name); return NULL; } } return combined_py_args; }
/* pygi_invoke_marshal_in_args: * * Fills out the state struct argument lists. arg_values will always hold * actual values marshaled either to or from Python and C. arg_pointers will * hold pointers (via v_pointer) to auxilary value storage. This will normally * point to values stored in arg_values. In the case of caller allocated * out args, arg_pointers[x].v_pointer will point to newly allocated memory. * arg_pointers inserts a level of pointer indirection between arg_values * and the argument list ffi receives when dealing with non-caller allocated * out arguments. * * For example: * [[ * void callee (int *i, int j) { *i = 50 - j; } * void caller () { * int i = 0; * callee (&i, 8); * } * * args[0] == &arg_pointers[0]; * arg_pointers[0].v_pointer == &arg_values[0]; * arg_values[0].v_int == 42; * * args[1] == &arg_values[1]; * arg_values[1].v_int == 8; * ]] * */ static gboolean _invoke_marshal_in_args (PyGIInvokeState *state, PyGIFunctionCache *function_cache) { PyGICallableCache *cache = (PyGICallableCache *) function_cache; gssize i; if (state->n_py_in_args > cache->n_py_args) { char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%s() takes exactly %zd argument(s) (%zd given)", full_name, cache->n_py_args, state->n_py_in_args); g_free (full_name); return FALSE; } for (i = 0; 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; switch (arg_cache->direction) { case PYGI_DIRECTION_FROM_PYTHON: /* The ffi argument points directly at memory in arg_values. */ state->ffi_args[i] = c_arg; if (arg_cache->meta_type == PYGI_META_ARG_TYPE_CLOSURE) { state->ffi_args[i]->v_pointer = state->user_data; continue; } else if (arg_cache->meta_type != PYGI_META_ARG_TYPE_PARENT) continue; if (arg_cache->py_arg_index >= state->n_py_in_args) { char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%s() takes exactly %zd argument(s) (%zd given)", full_name, cache->n_py_args, state->n_py_in_args); g_free (full_name); /* clean up all of the args we have already marshalled, * since invoke will not be called */ pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, i); return FALSE; } py_arg = PyTuple_GET_ITEM (state->py_in_args, arg_cache->py_arg_index); break; case PYGI_DIRECTION_BIDIRECTIONAL: if (arg_cache->meta_type != PYGI_META_ARG_TYPE_CHILD) { if (arg_cache->py_arg_index >= state->n_py_in_args) { char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "%s() takes exactly %zd argument(s) (%zd given)", full_name, cache->n_py_args, state->n_py_in_args); g_free (full_name); pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, i); return FALSE; } py_arg = PyTuple_GET_ITEM (state->py_in_args, arg_cache->py_arg_index); } /* Fall through */ case PYGI_DIRECTION_TO_PYTHON: /* arg_pointers always stores a pointer to the data to be marshaled "to python" * even in cases where arg_pointers is not being used as indirection between * ffi and arg_values. This gives a guarantee that out argument marshaling * (_invoke_marshal_out_args) can always rely on arg_pointers pointing to * the correct chunk of memory to marshal. */ state->args[i].arg_pointer.v_pointer = c_arg; if (arg_cache->is_caller_allocates) { /* In the case of caller allocated out args, we don't use * an extra level of indirection and state->args will point * directly at the data to be marshaled. However, as noted * above, arg_pointers will also point to this caller allocated * chunk of memory used by out argument marshaling. */ state->ffi_args[i] = c_arg; if (!_caller_alloc (arg_cache, c_arg)) { char *full_name = pygi_callable_cache_get_full_name (cache); PyErr_Format (PyExc_TypeError, "Could not caller allocate argument %zd of callable %s", i, full_name); g_free (full_name); pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, i); return FALSE; } } else { /* Non-caller allocated out args will use arg_pointers as an * extra level of indirection */ state->ffi_args[i] = &state->args[i].arg_pointer; } break; } 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) { gboolean success; gpointer cleanup_data = NULL; if (!arg_cache->allow_none && py_arg == Py_None) { PyErr_Format (PyExc_TypeError, "Argument %zd does not allow None as a value", i); pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, i); return FALSE; } success = arg_cache->from_py_marshaller (state, cache, arg_cache, py_arg, c_arg, &cleanup_data); state->args[i].arg_cleanup_data = cleanup_data; if (!success) { pygi_marshal_cleanup_args_from_py_parameter_fail (state, cache, i); return FALSE; } } } return TRUE; }
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; }