diff options
author | Tomeu Vizoso <tomeu@sugarlabs.org> | 2010-04-18 13:11:11 -0400 |
---|---|---|
committer | Tomeu Vizoso <tomeu@sugarlabs.org> | 2010-04-18 13:15:07 -0400 |
commit | 8b70faa7a9a32b9ea8862f28a503e38f496cfd89 (patch) | |
tree | 139df2373cdc05b308dfd87ea46699eee8999f13 | |
parent | e239faacb4798fe2d166233ca1a19a843a6225e3 (diff) | |
download | pygi-8b70faa7a9a32b9ea8862f28a503e38f496cfd89.tar.gz pygi-8b70faa7a9a32b9ea8862f28a503e38f496cfd89.tar.xz pygi-8b70faa7a9a32b9ea8862f28a503e38f496cfd89.zip |
Implement vfuncs.
https://bugzilla.gnome.org/show_bug.cgi?id=602736
-rw-r--r-- | gi/gimodule.c | 89 | ||||
-rw-r--r-- | gi/pygi-argument.c | 1 | ||||
-rw-r--r-- | gi/pygi-callbacks.c | 3 | ||||
-rw-r--r-- | gi/pygi-closure.c | 4 | ||||
-rw-r--r-- | gi/pygi-closure.h | 2 | ||||
-rw-r--r-- | gi/pygi-info.c | 86 | ||||
-rw-r--r-- | gi/pygi-info.h | 1 | ||||
-rw-r--r-- | gi/types.py | 44 | ||||
-rw-r--r-- | tests/test_gi.py | 18 |
9 files changed, 227 insertions, 21 deletions
diff --git a/gi/gimodule.c b/gi/gimodule.c index 8dd8ac5..8650fb3 100644 --- a/gi/gimodule.c +++ b/gi/gimodule.c @@ -132,6 +132,94 @@ _wrap_pyg_register_interface_info(PyObject *self, PyObject *args) Py_RETURN_NONE; } +static PyObject * +_wrap_pyg_hook_up_vfunc_implementation (PyObject *self, PyObject *args) +{ + PyGIBaseInfo *py_info; + PyObject *py_type; + PyObject *py_function; + gpointer implementor_class = NULL; + GType ancestor_g_type = 0; + GType implementor_gtype = 0; + gpointer *method_ptr = NULL; + int length, i; + GIBaseInfo *vfunc_info; + GIBaseInfo *ancestor_info; + GIStructInfo *struct_info; + gboolean is_interface = FALSE; + PyGICClosure *closure = NULL; + + if (!PyArg_ParseTuple(args, "O!O!O:hook_up_vfunc_implementation", + &PyGIBaseInfo_Type, &py_info, + &PyGTypeWrapper_Type, &py_type, + &py_function)) + return NULL; + + implementor_gtype = pyg_type_from_object(py_type); + g_assert(G_TYPE_IS_CLASSED(implementor_gtype)); + + vfunc_info = py_info->info; + ancestor_info = g_base_info_get_container(vfunc_info); + is_interface = g_base_info_get_type(ancestor_info) == GI_INFO_TYPE_INTERFACE; + + ancestor_g_type = g_registered_type_info_get_g_type( + (GIRegisteredTypeInfo *)ancestor_info); + + implementor_class = g_type_class_ref(implementor_gtype); + if (is_interface) { + GTypeInstance *implementor_iface_class; + implementor_iface_class = g_type_interface_peek(implementor_class, + ancestor_g_type); + g_type_class_unref (implementor_class); + implementor_class = implementor_iface_class; + + struct_info = g_interface_info_get_iface_struct ((GIInterfaceInfo*)ancestor_info); + } else + struct_info = g_object_info_get_class_struct ((GIObjectInfo*)ancestor_info); + + length = g_struct_info_get_n_fields(struct_info); + for (i = 0; i < length; i++) { + GIFieldInfo *field_info; + GITypeInfo *type_info; + GIBaseInfo *interface_info; + GICallbackInfo *callback_info; + gint offset; + + field_info = g_struct_info_get_field (struct_info, i); + + if (strcmp(g_base_info_get_name((GIBaseInfo*) field_info), + g_base_info_get_name((GIBaseInfo*) vfunc_info)) != 0) + continue; + + type_info = g_field_info_get_type (field_info); + if (g_type_info_get_tag (type_info) != GI_TYPE_TAG_INTERFACE) + continue; + + interface_info = g_type_info_get_interface (type_info); + g_assert(g_base_info_get_type(interface_info) == GI_INFO_TYPE_CALLBACK); + + callback_info = (GICallbackInfo*) interface_info; + offset = g_field_info_get_offset(field_info); + method_ptr = G_STRUCT_MEMBER_P(implementor_class, offset); + + closure = _pygi_make_native_closure((GICallableInfo*)callback_info, + GI_SCOPE_TYPE_NOTIFIED, py_function, NULL); + + *method_ptr = closure->closure; + + g_base_info_unref (interface_info); + g_base_info_unref (type_info); + g_base_info_unref (field_info); + + break; + } + + g_base_info_unref (struct_info); + g_type_class_unref (implementor_class); + + Py_RETURN_NONE; +} + static PyMethodDef _pygi_functions[] = { { "enum_add", (PyCFunction)_wrap_pyg_enum_add, METH_VARARGS | METH_KEYWORDS }, @@ -139,6 +227,7 @@ static PyMethodDef _pygi_functions[] = { { "set_object_has_new_constructor", (PyCFunction)_wrap_pyg_set_object_has_new_constructor, METH_VARARGS | METH_KEYWORDS }, { "register_interface_info", (PyCFunction)_wrap_pyg_register_interface_info, METH_VARARGS }, + { "hook_up_vfunc_implementation", (PyCFunction)_wrap_pyg_hook_up_vfunc_implementation, METH_VARARGS }, { NULL, NULL, 0 } }; diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c index df88a6c..4bedc82 100644 --- a/gi/pygi-argument.c +++ b/gi/pygi-argument.c @@ -1518,6 +1518,7 @@ _pygi_argument_to_object (GArgument *arg, break; } + case GI_INFO_TYPE_INTERFACE: case GI_INFO_TYPE_OBJECT: if (arg->v_pointer == NULL) { object = Py_None; diff --git a/gi/pygi-callbacks.c b/gi/pygi-callbacks.c index cf6ebd9..fb852ff 100644 --- a/gi/pygi-callbacks.c +++ b/gi/pygi-callbacks.c @@ -202,10 +202,9 @@ _pygi_create_callback (PyGIBaseInfo *function_info, return FALSE; } - /** Now actually build the closure **/ *closure_out = _pygi_make_native_closure((GICallableInfo *)callback_info, - callback_arg, + g_arg_info_get_scope(callback_arg), py_function, py_user_data); diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c index 0ad8fef..35373d2 100644 --- a/gi/pygi-closure.c +++ b/gi/pygi-closure.c @@ -170,7 +170,7 @@ void _pygi_invoke_closure_free(gpointer data) PyGICClosure* _pygi_make_native_closure (GICallableInfo* info, - GIArgInfo* arg_info, + GIScopeType scope, PyObject *py_function, gpointer py_user_data) { @@ -199,7 +199,7 @@ _pygi_make_native_closure (GICallableInfo* info, /* Give the closure the information it needs to determine when to free itself later */ - closure->scope = g_arg_info_get_scope(arg_info); + closure->scope = scope; return closure; } diff --git a/gi/pygi-closure.h b/gi/pygi-closure.h index c03e69d..550bf8a 100644 --- a/gi/pygi-closure.h +++ b/gi/pygi-closure.h @@ -48,7 +48,7 @@ void _pygi_closure_handle(ffi_cif *cif, void *result, void void _pygi_invoke_closure_free(gpointer user_data); PyGICClosure* _pygi_make_native_closure (GICallableInfo* info, - GIArgInfo* arg_info, + GIScopeType scope, PyObject *function, gpointer user_data); diff --git a/gi/pygi-info.c b/gi/pygi-info.c index 824e579..00d9e4d 100644 --- a/gi/pygi-info.c +++ b/gi/pygi-info.c @@ -213,8 +213,8 @@ _pygi_info_new (GIBaseInfo *info) PyErr_SetString(PyExc_NotImplementedError, "GISignalInfo bindings not implemented"); return NULL; case GI_INFO_TYPE_VFUNC: - PyErr_SetString(PyExc_NotImplementedError, "GIVFuncInfo bindings not implemented"); - return NULL; + type = &PyGIVFuncInfo_Type; + break; case GI_INFO_TYPE_PROPERTY: PyErr_SetString(PyExc_NotImplementedError, "GIPropertyInfo bindings not implemented"); return NULL; @@ -1709,12 +1709,49 @@ _wrap_g_object_info_get_constants (PyGIBaseInfo *self) return infos; } +static PyObject * +_wrap_g_object_info_get_vfuncs (PyGIBaseInfo *self) +{ + gssize n_infos; + PyObject *infos; + gssize i; + + n_infos = g_object_info_get_n_vfuncs((GIObjectInfo *)self->info); + + infos = PyTuple_New(n_infos); + if (infos == NULL) { + return NULL; + } + + for (i = 0; i < n_infos; i++) { + GIBaseInfo *info; + PyObject *py_info; + + info = (GIBaseInfo *)g_object_info_get_vfunc((GIObjectInfo *)self->info, i); + g_assert(info != NULL); + + py_info = _pygi_info_new(info); + + g_base_info_unref(info); + + if (py_info == NULL) { + Py_CLEAR(infos); + break; + } + + PyTuple_SET_ITEM(infos, i, py_info); + } + + return infos; +} + static PyMethodDef _PyGIObjectInfo_methods[] = { { "get_parent", (PyCFunction)_wrap_g_object_info_get_parent, METH_NOARGS }, { "get_methods", (PyCFunction)_wrap_g_object_info_get_methods, METH_NOARGS }, { "get_fields", (PyCFunction)_wrap_g_object_info_get_fields, METH_NOARGS }, { "get_interfaces", (PyCFunction)_wrap_g_object_info_get_interfaces, METH_NOARGS }, { "get_constants", (PyCFunction)_wrap_g_object_info_get_constants, METH_NOARGS }, + { "get_vfuncs", (PyCFunction)_wrap_g_object_info_get_vfuncs, METH_NOARGS }, { NULL, NULL, 0 } }; @@ -1794,9 +1831,46 @@ _wrap_g_interface_info_get_constants (PyGIBaseInfo *self) return infos; } +static PyObject * +_wrap_g_interface_info_get_vfuncs (PyGIBaseInfo *self) +{ + gssize n_infos; + PyObject *infos; + gssize i; + + n_infos = g_interface_info_get_n_vfuncs((GIInterfaceInfo *)self->info); + + infos = PyTuple_New(n_infos); + if (infos == NULL) { + return NULL; + } + + for (i = 0; i < n_infos; i++) { + GIBaseInfo *info; + PyObject *py_info; + + info = (GIBaseInfo *)g_interface_info_get_vfunc((GIInterfaceInfo *)self->info, i); + g_assert(info != NULL); + + py_info = _pygi_info_new(info); + + g_base_info_unref(info); + + if (py_info == NULL) { + Py_CLEAR(infos); + break; + } + + PyTuple_SET_ITEM(infos, i, py_info); + } + + return infos; +} + static PyMethodDef _PyGIInterfaceInfo_methods[] = { { "get_methods", (PyCFunction)_wrap_g_interface_info_get_methods, METH_NOARGS }, { "get_constants", (PyCFunction)_wrap_g_interface_info_get_constants, METH_NOARGS }, + { "get_vfuncs", (PyCFunction)_wrap_g_interface_info_get_vfuncs, METH_NOARGS }, { NULL, NULL, 0 } }; @@ -2102,6 +2176,13 @@ static PyMethodDef _PyGIUnresolvedInfo_methods[] = { { NULL, NULL, 0 } }; +/* GIVFuncInfo */ +_PyGI_DEFINE_INFO_TYPE("VFuncInfo", GIVFuncInfo, PyGIBaseInfo_Type); + +static PyMethodDef _PyGIVFuncInfo_methods[] = { + { NULL, NULL, 0 } +}; + /* Private */ gchar * @@ -2151,6 +2232,7 @@ _pygi_info_register_types (PyObject *m) _PyGI_REGISTER_TYPE(m, PyGIConstantInfo_Type, "ConstantInfo"); _PyGI_REGISTER_TYPE(m, PyGIValueInfo_Type, "ValueInfo"); _PyGI_REGISTER_TYPE(m, PyGIFieldInfo_Type, "FieldInfo"); + _PyGI_REGISTER_TYPE(m, PyGIVFuncInfo_Type, "VFuncInfo"); #undef _PyGI_REGISTER_TYPE } diff --git a/gi/pygi-info.h b/gi/pygi-info.h index 437ef9a..fe02d1a 100644 --- a/gi/pygi-info.h +++ b/gi/pygi-info.h @@ -45,6 +45,7 @@ extern PyTypeObject PyGIConstantInfo_Type; extern PyTypeObject PyGIValueInfo_Type; extern PyTypeObject PyGIFieldInfo_Type; extern PyTypeObject PyGIUnresolvedInfo_Type; +extern PyTypeObject PyGIVFuncInfo_Type; #define PyGIBaseInfo_GET_GI_INFO(object) g_base_info_ref(((PyGIBaseInfo *)object)->info) diff --git a/gi/types.py b/gi/types.py index b7061bd..cda1e52 100644 --- a/gi/types.py +++ b/gi/types.py @@ -30,7 +30,8 @@ from ._gi import \ ObjectInfo, \ StructInfo, \ set_object_has_new_constructor, \ - register_interface_info + register_interface_info, \ + hook_up_vfunc_implementation def Function(info): @@ -91,6 +92,22 @@ class MetaClassHelper(object): value = constant_info.get_value() setattr(cls, name, value) + def _setup_vfuncs(cls): + for base in cls.__bases__: + if not hasattr(base, '__info__') or \ + not hasattr(base.__info__, 'get_vfuncs'): + continue + for vfunc_info in base.__info__.get_vfuncs(): + vfunc = getattr(cls, 'do_' + vfunc_info.get_name(), None) + if vfunc is None: + raise TypeError('Class implementing %s.%s should implement ' + 'the method do_%s()' % (base.__info__.get_namespace(), + base.__info__.get_name(), + vfunc_info.get_name())) + else: + hook_up_vfunc_implementation(vfunc_info, cls.__gtype__, + vfunc) + class GObjectMeta(gobject.GObjectMeta, MetaClassHelper): @@ -98,19 +115,18 @@ class GObjectMeta(gobject.GObjectMeta, MetaClassHelper): super(GObjectMeta, cls).__init__(name, bases, dict_) # Avoid touching anything else than the base class. - if cls.__info__.get_g_type().pytype is not None: - return - - cls._setup_methods() - cls._setup_constants() - - if isinstance(cls.__info__, ObjectInfo): - cls._setup_fields() - cls._setup_constructors() - set_object_has_new_constructor(cls.__info__.get_g_type()) - elif isinstance(cls.__info__, InterfaceInfo): - register_interface_info(cls.__info__.get_g_type()) - + if cls.__info__.get_g_type().pytype is None: + cls._setup_methods() + cls._setup_constants() + + if isinstance(cls.__info__, ObjectInfo): + cls._setup_fields() + cls._setup_constructors() + set_object_has_new_constructor(cls.__info__.get_g_type()) + elif isinstance(cls.__info__, InterfaceInfo): + register_interface_info(cls.__info__.get_g_type()) + else: + cls._setup_vfuncs() class StructMeta(type, MetaClassHelper): diff --git a/tests/test_gi.py b/tests/test_gi.py index b37dce4..9f3c215 100644 --- a/tests/test_gi.py +++ b/tests/test_gi.py @@ -1313,10 +1313,17 @@ class TestPythonGObject(unittest.TestCase): class Object(GIMarshallingTests.Object): __gtype_name__ = "Object" + def __init__(self, int): + GIMarshallingTests.Object.__init__(self) + self.val = None + def method(self): # Don't call super, which asserts that self.int == 42. pass + def do_method_int8_in(self, int8): + self.val = int8 + def test_object(self): self.assertTrue(issubclass(self.Object, GIMarshallingTests.Object)) @@ -1326,6 +1333,11 @@ class TestPythonGObject(unittest.TestCase): def test_object_method(self): self.Object(int = 0).method() + def test_object_vfuncs(self): + object_ = self.Object(int = 42) + object_.method_int8_in(84) + self.assertEqual(object_.val, 84) + class TestMultiOutputArgs(unittest.TestCase): @@ -1351,12 +1363,18 @@ class TestInterfaces(unittest.TestCase): __gtype_name__ = 'TestInterfaceImpl' def __init__(self): gobject.GObject.__init__(self) + self.val = None + + def do_test_int8_in(self, int8): + self.val = int8 self.assertTrue(issubclass(TestInterfaceImpl, GIMarshallingTests.Interface)) instance = TestInterfaceImpl() self.assertTrue(isinstance(instance, GIMarshallingTests.Interface)) + GIMarshallingTests.test_interface_test_int8_in(instance, 42) + self.assertEquals(instance.val, 42) class TestOverrides(unittest.TestCase): |