diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | gi/Makefile.am | 4 | ||||
-rw-r--r-- | gi/pygi-argument.c | 12 | ||||
-rw-r--r-- | gi/pygi-callbacks.c | 216 | ||||
-rw-r--r-- | gi/pygi-callbacks.h | 47 | ||||
-rw-r--r-- | gi/pygi-closure.c | 205 | ||||
-rw-r--r-- | gi/pygi-closure.h | 57 | ||||
-rw-r--r-- | gi/pygi-info.c | 49 | ||||
-rw-r--r-- | gi/pygi-private.h | 2 | ||||
-rw-r--r-- | tests/test_gi.py | 64 |
10 files changed, 648 insertions, 10 deletions
diff --git a/configure.ac b/configure.ac index 3a596c3..544bcf7 100644 --- a/configure.ac +++ b/configure.ac @@ -33,6 +33,8 @@ CPPFLAGS+="${PYTHON_INCLUDES}" AC_CHECK_HEADER(Python.h, , AC_MSG_ERROR(Python headers not found)) CPPFLAGS="${save_CPPFLAGS}" +# FFI +PKG_CHECK_MODULES(FFI, libffi >= 3.0) # GNOME PKG_CHECK_MODULES(GNOME, diff --git a/gi/Makefile.am b/gi/Makefile.am index 0299d01..ff32761 100644 --- a/gi/Makefile.am +++ b/gi/Makefile.am @@ -42,6 +42,10 @@ _gi_la_SOURCES = \ pygi-type.h \ pygi-boxed.c \ pygi-boxed.h \ + pygi-closure.c \ + pygi-closure.h \ + pygi-callbacks.c \ + pygi-callbacks.h \ pygi.h \ pygi-private.h \ pygobject-external.h \ diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c index 335074e..0737bb7 100644 --- a/gi/pygi-argument.c +++ b/gi/pygi-argument.c @@ -29,6 +29,7 @@ #include <datetime.h> #include <pygobject.h> + static void _pygi_g_type_tag_py_bounds (GITypeTag type_tag, PyObject **lower, @@ -379,8 +380,11 @@ check_number_release: switch (info_type) { case GI_INFO_TYPE_CALLBACK: - /* TODO */ - PyErr_SetString(PyExc_NotImplementedError, "callback marshalling is not supported yet"); + if (!PyCallable_Check(object)) { + PyErr_Format(PyExc_TypeError, "Must be callable, not %s", + object->ob_type->tp_name); + retval = 0; + } break; case GI_INFO_TYPE_ENUM: retval = _pygi_g_registered_type_info_check_object( @@ -882,8 +886,8 @@ array_item_error: switch (info_type) { case GI_INFO_TYPE_CALLBACK: - PyErr_SetString(PyExc_NotImplementedError, "callback marshalling is not supported yet"); - /* TODO */ + /* This should be handled in invoke() */ + g_assert_not_reached(); break; case GI_INFO_TYPE_BOXED: case GI_INFO_TYPE_STRUCT: diff --git a/gi/pygi-callbacks.c b/gi/pygi-callbacks.c new file mode 100644 index 0000000..0096a8b --- /dev/null +++ b/gi/pygi-callbacks.c @@ -0,0 +1,216 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * pygi-closure.c: PyGI C Closure functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "pygi-private.h" + +static PyGICClosure *global_destroy_notify; + +static void +_pygi_destroy_notify_callback_closure(ffi_cif *cif, + void *result, + void **args, + void *data) +{ + PyGICClosure *info = *(void**)(args[0]); + + g_assert(info); + + _pygi_invoke_closure_free(info); +} + + +PyGICClosure* +_pygi_destroy_notify_create(void) +{ + if (!global_destroy_notify) { + + ffi_status status; + PyGICClosure *destroy_notify = g_slice_new0(PyGICClosure); + + g_assert(destroy_notify); + + GIBaseInfo* glib_destroy_notify = g_irepository_find_by_name(NULL, "GLib", "DestroyNotify"); + g_assert(glib_destroy_notify != NULL); + g_assert(g_base_info_get_type(glib_destroy_notify) == GI_INFO_TYPE_CALLBACK); + + destroy_notify->closure = g_callable_info_prepare_closure((GICallableInfo*)glib_destroy_notify, + &destroy_notify->cif, + _pygi_destroy_notify_callback_closure, + NULL); + + global_destroy_notify = destroy_notify; + } + + return global_destroy_notify; +} + + +gboolean +_pygi_scan_for_callbacks (PyGIBaseInfo *function_info, + gboolean is_method, + guint8 *callback_index, + guint8 *user_data_index, + guint8 *destroy_notify_index) +{ + guint i, n_args; + + *callback_index = G_MAXUINT8; + *user_data_index = G_MAXUINT8; + *destroy_notify_index = G_MAXUINT8; + + n_args = g_callable_info_get_n_args((GICallableInfo *)function_info->info); + for (i = 0; i < n_args; i++) { + GIDirection direction; + GIArgInfo *arg_info; + GITypeInfo *type_info; + guint8 destroy, closure; + GITypeTag type_tag; + + arg_info = g_callable_info_get_arg((GICallableInfo*) function_info->info, i); + type_info = g_arg_info_get_type(arg_info); + type_tag = g_type_info_get_tag(type_info); + + if (type_tag == GI_TYPE_TAG_INTERFACE) { + GIBaseInfo* interface_info; + GIInfoType interface_type; + + interface_info = g_type_info_get_interface(type_info); + interface_type = g_base_info_get_type(interface_info); + if (interface_type == GI_INFO_TYPE_CALLBACK && + !(strcmp(g_base_info_get_namespace((GIBaseInfo*) interface_info), "GLib") == 0 && + strcmp(g_base_info_get_name((GIBaseInfo*) interface_info), "DestroyNotify") == 0)) { + if (*callback_index != G_MAXUINT8) { + PyErr_Format(PyExc_TypeError, "Function %s.%s has multiple callbacks, not supported", + g_base_info_get_namespace((GIBaseInfo*) function_info->info), + g_base_info_get_name((GIBaseInfo*) function_info->info)); + g_base_info_unref(interface_info); + return FALSE; + } + *callback_index = i; + } + g_base_info_unref(interface_info); + } + destroy = g_arg_info_get_destroy(arg_info); + if (is_method) + --destroy; + closure = g_arg_info_get_closure(arg_info); + if (is_method) + --closure; + direction = g_arg_info_get_direction(arg_info); + + if (destroy > 0 && destroy < n_args) { + if (*destroy_notify_index != G_MAXUINT8) { + PyErr_Format(PyExc_TypeError, "Function %s has multiple GDestroyNotify, not supported", + g_base_info_get_name((GIBaseInfo*)function_info->info)); + return FALSE; + } + *destroy_notify_index = destroy; + } + + if (closure > 0 && closure < n_args) { + if (*user_data_index != G_MAXUINT8) { + PyErr_Format(PyExc_TypeError, "Function %s has multiple user_data arguments, not supported", + g_base_info_get_name((GIBaseInfo*)function_info->info)); + return FALSE; + } + *user_data_index = closure; + } + + g_base_info_unref((GIBaseInfo*)arg_info); + g_base_info_unref((GIBaseInfo*)type_info); + } + + return TRUE; +} + +gboolean +_pygi_create_callback (PyGIBaseInfo *function_info, + gboolean is_method, + 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; + + callback_arg = g_callable_info_get_arg((GICallableInfo*) function_info->info, callback_index); + scope = g_arg_info_get_scope(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) + 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); + 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: Invalid callback given for argument %s", + g_base_info_get_namespace((GIBaseInfo*) function_info->info), + g_base_info_get_name((GIBaseInfo*) function_info->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, + callback_arg, + py_function, + py_user_data); + + g_base_info_unref((GIBaseInfo*) callback_info); + g_base_info_unref((GIBaseInfo*) callback_type); + + return TRUE; +} diff --git a/gi/pygi-callbacks.h b/gi/pygi-callbacks.h new file mode 100644 index 0000000..7b3401d --- /dev/null +++ b/gi/pygi-callbacks.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifndef __PYGI_CALLBACKS_H__ +#define __PYGI_CALLBACKS_H__ + +G_BEGIN_DECLS + +void _pygi_callback_notify_info_free(gpointer user_data); + +PyGICClosure*_pygi_destroy_notify_create(void); + +gboolean _pygi_scan_for_callbacks (PyGIBaseInfo *self, + gboolean is_method, + guint8 *callback_index, + guint8 *user_data_index, + guint8 *destroy_notify_index); + +gboolean _pygi_create_callback (PyGIBaseInfo *self, + gboolean is_method, + 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); + +G_END_DECLS + +#endif diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c new file mode 100644 index 0000000..0ad8fef --- /dev/null +++ b/gi/pygi-closure.c @@ -0,0 +1,205 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * pygi-closure.c: PyGI C Closure functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "pygi-private.h" + +/* This maintains a list of closures which can be free'd whenever + as they have been called. We will free them on the next + library function call. + */ +static GSList* async_free_list; + +void +_pygi_closure_handle (ffi_cif *cif, + void *result, + void **args, + void *data) +{ + PyGILState_STATE state; + PyGICClosure *closure = data; + gint n_args, i; + GIArgInfo *arg_info; + GIDirection arg_direction; + GITypeInfo *arg_type; + GITransfer arg_transfer; + GITypeTag arg_tag; + GITypeTag return_tag; + GITransfer return_transfer; + GITypeInfo *return_type; + PyObject *retval; + PyObject *py_args; + PyObject *pyarg; + gint n_in_args, n_out_args; + + + /* Lock the GIL as we are coming into this code without the lock and we + may be executing python code */ + state = PyGILState_Ensure(); + + return_type = g_callable_info_get_return_type(closure->info); + return_tag = g_type_info_get_tag(return_type); + return_transfer = g_callable_info_get_caller_owns(closure->info); + + n_args = g_callable_info_get_n_args (closure->info); + + py_args = PyTuple_New(n_args); + if (py_args == NULL) { + PyErr_Clear(); + goto end; + } + + n_in_args = 0; + + for (i = 0; i < n_args; i++) { + arg_info = g_callable_info_get_arg (closure->info, i); + arg_type = g_arg_info_get_type (arg_info); + arg_transfer = g_arg_info_get_ownership_transfer(arg_info); + arg_tag = g_type_info_get_tag(arg_type); + arg_direction = g_arg_info_get_direction(arg_info); + switch (arg_tag){ + case GI_TYPE_TAG_VOID: + { + if (g_type_info_is_pointer(arg_type)) { + if (PyTuple_SetItem(py_args, n_in_args, closure->user_data) != 0) { + PyErr_Clear(); + goto end; + } + n_in_args++; + continue; + } + } + case GI_TYPE_TAG_ERROR: + { + continue; + } + default: + { + pyarg = _pygi_argument_to_object (args[i], + arg_type, + arg_transfer); + + if(PyTuple_SetItem(py_args, n_in_args, pyarg) != 0) { + PyErr_Clear(); + goto end; + } + n_in_args++; + g_base_info_unref((GIBaseInfo*)arg_info); + g_base_info_unref((GIBaseInfo*)arg_type); + } + } + + } + + if(_PyTuple_Resize (&py_args, n_in_args) != 0) { + PyErr_Clear(); + goto end; + } + + retval = PyObject_CallObject((PyObject *)closure->function, py_args); + + Py_DECREF(py_args); + + if (retval == NULL) { + goto end; + } + + *(GArgument*)result = _pygi_argument_from_object(retval, return_type, return_transfer); + +end: + g_base_info_unref((GIBaseInfo*)return_type); + + PyGILState_Release(state); + + if (closure->user_data) + Py_XDECREF(closure->user_data); + + /* Now that the closure has finished we can make a decision about how + to free it. Scope call gets free'd now, scope notified will be freed + when the notify is called and we can free async anytime we want + once we return from this function */ + switch (closure->scope) { + case GI_SCOPE_TYPE_CALL: + _pygi_invoke_closure_free(closure); + break; + case GI_SCOPE_TYPE_NOTIFIED: + break; + case GI_SCOPE_TYPE_ASYNC: + /* Append this PyGICClosure to a list of closure that we will free + after we're done with this function invokation */ + async_free_list = g_slist_prepend(async_free_list, closure); + break; + default: + g_assert_not_reached(); + } +} + +void _pygi_invoke_closure_free(gpointer data) +{ + PyGICClosure* invoke_closure = (PyGICClosure *)data; + + + Py_DECREF(invoke_closure->function); + + g_callable_info_free_closure(invoke_closure->info, + invoke_closure->closure); + + if (invoke_closure->info) + g_base_info_unref((GIBaseInfo*)invoke_closure->info); + + g_slice_free(PyGICClosure, invoke_closure); +} + + +PyGICClosure* +_pygi_make_native_closure (GICallableInfo* info, + GIArgInfo* arg_info, + PyObject *py_function, + gpointer py_user_data) +{ + PyGICClosure *closure; + ffi_closure *fficlosure; + + /* Begin by cleaning up old async functions */ + g_slist_foreach(async_free_list, (GFunc)_pygi_invoke_closure_free, NULL); + g_slist_free(async_free_list); + async_free_list = NULL; + + /* Build the closure itself */ + closure = g_slice_new0(PyGICClosure); + closure->info = (GICallableInfo *) g_base_info_ref ((GIBaseInfo *) info); + closure->function = py_function; + closure->user_data = py_user_data; + + Py_INCREF(py_function); + if (closure->user_data) + Py_INCREF(closure->user_data); + + fficlosure = + g_callable_info_prepare_closure (info, &closure->cif, _pygi_closure_handle, + closure); + closure->closure = fficlosure; + + /* Give the closure the information it needs to determine when + to free itself later */ + closure->scope = g_arg_info_get_scope(arg_info); + + return closure; +} diff --git a/gi/pygi-closure.h b/gi/pygi-closure.h new file mode 100644 index 0000000..c03e69d --- /dev/null +++ b/gi/pygi-closure.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; c-basic-offset: 4 -*- + * vim: tabstop=4 shiftwidth=4 expandtab + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#ifndef __PYGI_CLOSURE_H__ +#define __PYGI_CLOSURE_H__ + +#include <Python.h> +#include <girffi.h> +#include <ffi.h> + +G_BEGIN_DECLS + + +/* Private */ + +typedef struct _PyGICClosure +{ + GICallableInfo *info; + PyObject *function; + + ffi_closure *closure; + ffi_cif cif; + + GIScopeType scope; + + PyObject* user_data; +} PyGICClosure; + +void _pygi_closure_handle(ffi_cif *cif, void *result, void + **args, void *userdata); + +void _pygi_invoke_closure_free(gpointer user_data); + +PyGICClosure* _pygi_make_native_closure (GICallableInfo* info, + GIArgInfo* arg_info, + PyObject *function, + gpointer user_data); + +G_END_DECLS + +#endif /* __PYGI_CLOSURE_H__ */ diff --git a/gi/pygi-info.c b/gi/pygi-info.c index 18981a2..dbcf999 100644 --- a/gi/pygi-info.c +++ b/gi/pygi-info.c @@ -503,6 +503,11 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self, gsize n_aux_out_args; gsize n_return_values; + guint8 callback_index; + guint8 user_data_index; + guint8 destroy_notify_index; + PyGICClosure *closure; + glong error_arg_pos; GIArgInfo **arg_infos; @@ -538,6 +543,10 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self, n_backup_args = 0; n_aux_in_args = 0; n_aux_out_args = 0; + + /* Check the argument count. */ + n_py_args = PyTuple_Size(py_args); + g_assert(n_py_args >= 0); error_arg_pos = -1; @@ -547,11 +556,32 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self, args_is_auxiliary = g_newa(gboolean, n_args); memset(args_is_auxiliary, 0, sizeof(args_is_auxiliary) * n_args); + if (!_pygi_scan_for_callbacks (self, is_method, &callback_index, &user_data_index, + &destroy_notify_index)) + return NULL; + + if (callback_index != G_MAXUINT8) { + if (!_pygi_create_callback (self, is_method, + n_args, n_py_args, py_args, callback_index, + user_data_index, + destroy_notify_index, &closure)) + return NULL; + + args_is_auxiliary[callback_index] = FALSE; + if (destroy_notify_index != G_MAXUINT8) { + args_is_auxiliary[destroy_notify_index] = TRUE; + n_aux_in_args += 1; + } + } + if (is_method) { /* The first argument is the instance. */ n_in_args += 1; } + /* We do a first (well, second) pass here over the function to scan for special cases. + * This is currently array+length combinations and GError. + */ for (i = 0; i < n_args; i++) { GIDirection direction; GITransfer transfer; @@ -559,7 +589,7 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self, arg_infos[i] = g_callable_info_get_arg((GICallableInfo *)self->info, i); arg_type_infos[i] = g_arg_info_get_type(arg_infos[i]); - + direction = g_arg_info_get_direction(arg_infos[i]); transfer = g_arg_info_get_ownership_transfer(arg_infos[i]); arg_type_tag = g_type_info_get_tag(arg_type_infos[i]); @@ -639,10 +669,6 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self, gsize n_py_args_expected; Py_ssize_t py_args_pos; - /* Check the argument count. */ - n_py_args = PyTuple_Size(py_args); - g_assert(n_py_args >= 0); - n_py_args_expected = n_in_args + (is_constructor ? 1 : 0) - n_aux_in_args @@ -799,6 +825,19 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self, for (i = 0; i < n_args; i++) { GIDirection direction; + if (i == callback_index) { + args[i]->v_pointer = closure->closure; + py_args_pos++; + continue; + } else if (i == user_data_index) { + args[i]->v_pointer = closure; + py_args_pos++; + continue; + } else if (i == destroy_notify_index) { + args[i]->v_pointer = _pygi_destroy_notify_create(); + continue; + } + if (args_is_auxiliary[i]) { continue; } diff --git a/gi/pygi-private.h b/gi/pygi-private.h index 9f39d0d..74716be 100644 --- a/gi/pygi-private.h +++ b/gi/pygi-private.h @@ -25,6 +25,8 @@ #include "pygi-argument.h" #include "pygi-type.h" #include "pygi-foreign.h" +#include "pygi-closure.h" +#include "pygi-callbacks.h" G_BEGIN_DECLS diff --git a/tests/test_gi.py b/tests/test_gi.py index 9b9a849..a9076b6 100644 --- a/tests/test_gi.py +++ b/tests/test_gi.py @@ -12,7 +12,7 @@ from datetime import datetime import sys sys.path.insert(0, "../") -from gi.repository import GIMarshallingTests +from gi.repository import GIMarshallingTests, Everything CONSTANT_UTF8 = "const \xe2\x99\xa5 utf8" @@ -1399,3 +1399,65 @@ class TestOverrides(unittest.TestCase): object_ = GIMarshallingTests.overrides_object_return() self.assertTrue(isinstance(object_, GIMarshallingTests.OverridesObject)) + + +class TestCallbacks(unittest.TestCase): + called = False + def testCallback(self): + TestCallbacks.called = False + def callback(): + TestCallbacks.called = True + + Everything.test_simple_callback(callback) + self.assertTrue(TestCallbacks.called) + + def testCallbackException(self): + """ + This test ensures that we get errors from callbacks correctly + and in particular that we do not segv when callbacks fail + """ + def callback(): + x = 1 / 0 + + try: + Everything.test_simple_callback(callback) + except ZeroDivisionError: + pass + + def testDoubleCallbackException(self): + """ + This test ensures that we get errors from callbacks correctly + and in particular that we do not segv when callbacks fail + """ + def badcallback(): + x = 1 / 0 + + def callback(): + GIMarshallingTests.boolean_return_true() + GIMarshallingTests.boolean_return_false() + Everything.test_simple_callback(badcallback()) + + try: + Everything.test_simple_callback(callback) + except ZeroDivisionError: + pass + + def testReturnValueCallback(self): + TestCallbacks.called = False + def callback(): + TestCallbacks.called = True + return 44 + + self.assertEquals(Everything.test_callback(callback), 44) + self.assertTrue(TestCallbacks.called) + + def testCallbackAsync(self): + TestCallbacks.called = False + def callback(foo): + TestCallbacks.called = True + return foo + + Everything.test_callback_async(callback, 44); + i = Everything.test_callback_thaw_async(); + self.assertEquals(44, i); + self.assertTrue(TestCallbacks.called) |