[pybank] Add support for structs



commit 0dbfcea29919e7fd22bb2f2e642a63ec08f59077
Author: Tomeu Vizoso <tomeu sugarlabs org>
Date:   Thu May 7 17:18:13 2009 +0200

    Add support for structs
---
 bank/bank-argument.c   |   21 +++-
 bank/bank-info.c       |  361 +++++++++++++++++++++++++++++++++---------------
 bank/bank.c            |    1 +
 bank/btypes.py         |   44 ++++++
 bank/module.py         |   37 +++++-
 everything_unittest.py |    5 +-
 6 files changed, 353 insertions(+), 116 deletions(-)

diff --git a/bank/bank-argument.c b/bank/bank-argument.c
index 0920484..a4265d4 100644
--- a/bank/bank-argument.c
+++ b/bank/bank-argument.c
@@ -109,11 +109,30 @@ pyg_argument_to_pyobject(GArgument *arg, GITypeInfo *type_info)
 {
     GITypeTag type_tag;
     PyObject *obj;
+    GIBaseInfo* interface_info;
+    GIInfoType interface_type;
     
     g_return_val_if_fail(type_info != NULL, NULL);
     type_tag = g_type_info_get_tag(type_info);
     if ( type_tag == GI_TYPE_TAG_INTERFACE ) {
-	if ( arg->v_pointer == NULL ) {
+        interface_info = g_type_info_get_interface(type_info);
+        interface_type = g_base_info_get_type(interface_info);
+
+        if (interface_type == GI_INFO_TYPE_STRUCT || interface_type == GI_INFO_TYPE_BOXED) {
+	    // Create new struct based on arg->v_pointer
+	    const gchar *module_name = g_base_info_get_namespace(interface_info);
+	    const gchar *type_name = g_base_info_get_name(interface_info);
+	    PyObject *module = PyImport_ImportModule(module_name);
+	    PyTypeObject *tp = (PyTypeObject *)PyObject_GetAttrString(module, type_name);
+
+	    // TODO: Pass a reference to the arg->v_pointer
+	    obj = PyObject_GC_New(PyObject, tp);
+	    if (obj == NULL)
+		return NULL;
+	    PyObject_GC_Track(obj);
+	    Py_INCREF(obj);
+	    return obj;
+	} else if ( arg->v_pointer == NULL ) {
 	    obj = Py_None;
 	    Py_INCREF(obj);
 	    return obj;
diff --git a/bank/bank-info.c b/bank/bank-info.c
index 77f6be4..62f3b5b 100644
--- a/bank/bank-info.c
+++ b/bank/bank-info.c
@@ -327,134 +327,250 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
 {
     GArgument *in_args;
     GArgument *out_args;
+    GArgument *out_values;
     GArgument return_arg;
-    gboolean is_method;
-    int n_in_args;
-    int n_out_args;
+    int n_args;
+    int expected_in_argc;
+    int expected_out_argc;
     int i;
-    PyObject *py_arg;
-    GIDirection direction;
-    GError *error = NULL;
-    GIArgInfo *arg_info;
+    int argv_pos;
+    int in_args_pos;
+    int out_args_pos;
+    GError *error;
+    gboolean failed;
+    GIFunctionInfoFlags flags;
+    gboolean is_method;
+    gboolean invoke_ok;
+    GITypeInfo *return_info;
+    GITypeTag return_tag;
+    PyObject **return_values;
+    int n_return_values;
+    int next_rval;
     PyObject *retval;
-    int flags;
+    PyObject *py_arg;
 
     flags = g_function_info_get_flags((GIFunctionInfo*)self->info);
-    is_method = (flags & GI_FUNCTION_IS_METHOD) != 0 &&
-	(flags & GI_FUNCTION_IS_CONSTRUCTOR) == 0;
-    
-    n_in_args = is_method ? 1 : 0;
-    n_out_args = 0;
-    for (i = 0; i < g_callable_info_get_n_args((GICallableInfo*)self->info); i++) {
-	arg_info = g_callable_info_get_arg((GICallableInfo*)self->info, i);
-	direction = g_arg_info_get_direction(arg_info);
-	if (direction == GI_DIRECTION_IN) {
-	    n_in_args++;
-	} else if (direction == GI_DIRECTION_OUT) {
-	    n_out_args++;
-	} else if (direction == GI_DIRECTION_INOUT) {
-	    /* FIXME - just map these as in args temporarily, we need to
-	     * get gtk_init_check etc. working */
-	    n_in_args++;
-	    n_out_args++;
-	}
-	g_base_info_unref((GIBaseInfo*)arg_info);
+    is_method = (flags & GI_FUNCTION_IS_METHOD) != 0;
+
+    expected_in_argc = 0;
+    expected_out_argc = 0;
+
+    n_args = g_callable_info_get_n_args( (GICallableInfo*) self->info);
+    for (i = 0; i < n_args; i++) {
+        GIDirection direction;
+        GIArgInfo *arg_info;
+
+        arg_info = g_callable_info_get_arg( (GICallableInfo*) self->info, i);
+        direction = g_arg_info_get_direction(arg_info);
+        if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT)
+            expected_in_argc += 1;
+        if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT)
+            expected_out_argc += 1;
+        g_base_info_unref( (GIBaseInfo*) arg_info);
     }
 
-    in_args = g_new0(GArgument, n_in_args);
-    out_args = g_new0(GArgument, n_out_args);
+    g_debug("Call is to %s %s.%s with expected: %d in args, %d out args, %d total args",
+                      is_method ? "method" : "function",
+                      g_base_info_get_namespace( (GIBaseInfo*) self->info),
+                      g_base_info_get_name( (GIBaseInfo*) self->info),
+                      expected_in_argc,
+                      expected_out_argc,
+                      n_args);
+
+    if (is_method)
+        expected_in_argc += 1;
+
+    in_args = g_newa(GArgument, expected_in_argc);
+    out_args = g_newa(GArgument, expected_out_argc);
+    /* each out arg is a pointer, they point to these values */
+    out_values = g_newa(GArgument, expected_out_argc);
+
+    failed = FALSE;
+    in_args_pos = 0; /* index into in_args */
+    out_args_pos = 0; /* into out_args */
+    argv_pos = 0; /* index into argv */
+
     if (is_method) {
-	py_arg = PyTuple_GetItem(args, 0);
-	if (!py_arg)
-	    return NULL;
-	if (py_arg == Py_None)
-	    in_args[0].v_pointer = NULL;
-	else
+        GIBaseInfo *container = g_base_info_get_container((GIBaseInfo *) self->info);
+        GIInfoType type = g_base_info_get_type(container);
+
+        py_arg = PyTuple_GetItem(args, 0);
+        if (!py_arg)
+            return NULL;
+        if (py_arg == Py_None)
+            in_args[0].v_pointer = NULL;
+        else if (type == GI_INFO_TYPE_STRUCT || type == GI_INFO_TYPE_BOXED) {
+	    PyObject *buffer = PyObject_GetAttrString((PyObject *)py_arg,
+	                                                  "__buffer__");
+	    in_args[0].v_pointer = PyString_AsString(buffer);
+        } else { /* by fallback is always object */
 	    in_args[0].v_pointer = pygobject_get(py_arg);
+        }
+        ++in_args_pos;
     }
-    for (i = 0; i < g_callable_info_get_n_args((GICallableInfo*)self->info); i++) {
-	arg_info = g_callable_info_get_arg((GICallableInfo*)self->info, i);
-	direction = g_arg_info_get_direction(arg_info);
-	int offset = i + (is_method ? 1 : 0);
-	if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
-	    py_arg = PyTuple_GetItem(args, offset);
-	    if (!py_arg)
-		return NULL;
-	    in_args[offset] = pyg_argument_from_pyobject(py_arg, arg_info);
-	}
-	
-	g_base_info_unref((GIBaseInfo*)arg_info);
+
+    for (i = 0; i < n_args; i++) {
+        GIDirection direction;
+        GIArgInfo *arg_info;
+        GArgument *out_value;
+
+        arg_info = g_callable_info_get_arg( (GICallableInfo*) self->info, i);
+        direction = g_arg_info_get_direction(arg_info);
+
+        out_value = NULL;
+        if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
+            g_assert(out_args_pos < expected_out_argc);
+
+            out_value = &out_values[out_args_pos];
+            out_args[out_args_pos].v_pointer = out_value;
+            ++out_args_pos;
+        }
+
+        if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
+            py_arg = PyTuple_GetItem(args, i);
+	    GArgument in_value = pyg_argument_from_pyobject(py_arg, arg_info);
+
+            ++argv_pos;
+
+            if (direction == GI_DIRECTION_IN) {
+                in_args[in_args_pos] = in_value;
+            } else {
+                /* INOUT means we pass a pointer */
+                g_assert(out_value != NULL);
+                *out_value = in_value;
+                in_args[in_args_pos].v_pointer = out_value;
+            }
+
+            ++in_args_pos;
+        }
+
+        g_base_info_unref( (GIBaseInfo*) arg_info);
+
+        if (failed)
+            break;
+    }
+
+    if (failed) {
+	g_error("Failed to convert all args.");
+        return NULL;
+    }
+
+    g_assert(in_args_pos == expected_in_argc);
+    g_assert(out_args_pos == expected_out_argc);
+
+    error = NULL;
+    invoke_ok = g_function_info_invoke( (GIFunctionInfo*) self->info,
+                                        in_args, expected_in_argc,
+                                        out_args, expected_out_argc,
+                                        &return_arg,
+                                        &error);
+
+    /* Return value and out arguments are valid only if invocation doesn't
+     * return error. In arguments need to be released always.
+     */
+    failed = FALSE;
+
+    return_info = g_callable_info_get_return_type( (GICallableInfo*) self->info);
+    g_assert(return_info != NULL);
+
+    return_tag = g_type_info_get_tag(return_info);
+
+    retval = NULL;
+
+    next_rval = 0; /* index into return_values */
+
+    n_return_values = expected_out_argc;
+    if (return_tag != GI_TYPE_TAG_VOID)
+        n_return_values += 1;
+
+    return_values = NULL; /* Quiet gcc warning about initialization */
+    if (n_return_values > 0) {
+        if (invoke_ok) {
+            return_values = g_newa(PyObject*, n_return_values);
+        }
+
+        if (return_tag != GI_TYPE_TAG_VOID) {
+	    return_values[next_rval] = pyg_argument_to_pyobject(&return_arg, return_info);
+
+            ++next_rval;
+        }
+    }
+
+    /* We walk over all args, release in args (if allocated) and convert
+     * all out args
+     */
+    in_args_pos = is_method ? 1 : 0; /* index into in_args */
+    out_args_pos = 0; /* into out_args */
+
+    for (i = 0; i < n_args; i++) {
+        GIDirection direction;
+        GIArgInfo *arg_info;
+        GITypeInfo *arg_type_info;
+
+        arg_info = g_callable_info_get_arg( (GICallableInfo*) self->info, i);
+        direction = g_arg_info_get_direction(arg_info);
+
+        arg_type_info = g_arg_info_get_type(arg_info);
+
+        if (direction == GI_DIRECTION_IN) {
+            g_assert(in_args_pos < expected_in_argc);
+
+            ++in_args_pos;
+        } else {
+            /* INOUT or OUT */
+            if (direction == GI_DIRECTION_INOUT)
+                g_assert(in_args_pos < expected_in_argc);
+            g_assert(next_rval < n_return_values);
+            g_assert(out_args_pos < expected_out_argc);
+
+	    return_values[next_rval] = pyg_argument_to_pyobject(&out_args[out_args_pos], arg_type_info);
+
+            if (direction == GI_DIRECTION_INOUT)
+                ++in_args_pos;
+
+            ++out_args_pos;
+
+            ++next_rval;
+        }
+
+        g_base_info_unref( (GIBaseInfo*) arg_type_info);
+        g_base_info_unref( (GIBaseInfo*) arg_info);
     }
 
-    if (g_function_info_invoke((GIFunctionInfo*)self->info, 
-			       in_args, n_in_args,
-			       out_args, n_out_args,
-			       &return_arg,
-			       &error)) {
-	GITypeInfo *return_info;
-	GITypeTag type_tag;
-
-	return_info = g_callable_info_get_return_type
-	    ((GICallableInfo*)self->info);
-
-	type_tag = g_type_info_get_tag((GITypeInfo*)return_info);
-
-	if (n_out_args == 1) {
-	    retval = pyg_argument_to_pyobject(&return_arg, return_info);
-	} else {
-	    PyObject *tuple;
-	    PyObject *item;
-	    int j = 0;
-	    int last = 0;
-	    int n_args;
-	    int start;
-
-	    
-	    if (n_out_args > 0) {
-		if (g_type_info_get_tag((GITypeInfo*)return_info) != GI_TYPE_TAG_VOID) {
-		    item = pyg_argument_to_pyobject(&return_arg, return_info);
-		    tuple = PyTuple_New(n_out_args+1);
-		    PyTuple_SetItem(tuple, 0, item);
-		    start = 1;
-		} else {
-		    tuple = PyTuple_New(n_out_args);
-		    start = 0;
-		}
-		    
-		n_args = g_callable_info_get_n_args((GICallableInfo*)self->info);
-		for (i = 0; i < n_out_args; i++) {
-		    
-		    if (last >= n_args)
-			break;
-			    
-		    for (j = last; j < n_args; j++) {
-			arg_info = g_callable_info_get_arg((GICallableInfo*)self->info, j);
-			direction = g_arg_info_get_direction(arg_info);
-			if (direction != GI_DIRECTION_IN) {
-			    GITypeInfo *type_info;
-			    type_info = g_arg_info_get_type(arg_info);
-			    item = pyg_argument_to_pyobject(&out_args[i], type_info);
-			    PyTuple_SetItem(tuple, i+start, item);
-			    last = j;
-			    g_base_info_unref((GIBaseInfo*)type_info);
-			}
-			g_base_info_unref((GIBaseInfo*)arg_info);
-		    }
-		}
-		retval = tuple;
-	    } else {
-		retval = pyg_argument_to_pyobject(&return_arg, return_info);
+    g_assert(next_rval == n_return_values);
+    g_assert(out_args_pos == expected_out_argc);
+    g_assert(in_args_pos == expected_in_argc);
+
+    if (invoke_ok && n_return_values > 0) {
+ 	if (n_return_values == 0) {
+	    retval = Py_None;
+	    Py_INCREF(retval);
+        } else if (n_return_values == 1) {
+            retval = return_values[0];
+        } else {
+            retval = PyTuple_New(n_return_values);
+	    for (i = 0; i < n_return_values; i++) {	    
+		PyTuple_SetItem(retval, i, return_values[i]);
 	    }
-	}
-	g_base_info_unref((GIBaseInfo*)return_info);
+        }
+    }
+
+    g_base_info_unref( (GIBaseInfo*) return_info);
+
+    if (invoke_ok) {
+        return failed ? NULL : retval;
     } else {
-	g_print("Error: %s\n", error->message);
-	
+        g_assert(error != NULL);
+        g_error("Error invoking %s.%s: %s",
+                g_base_info_get_namespace( (GIBaseInfo*) self->info),
+                g_base_info_get_name( (GIBaseInfo*) self->info),
+                error->message);
+        g_error_free(error);
+
 	retval = Py_None;
 	Py_INCREF(retval);
+        return retval;
     }
-
-    return retval;
 }
 
 static PyMethodDef _PyGIFunctionInfo_methods[] = {
@@ -490,7 +606,30 @@ static PyMethodDef _PyGIRegisteredTypeInfo_methods[] = {
 
 /* GIStructInfo */
 NEW_CLASS("StructInfo", GIStructInfo);
+
+static PyObject *
+_wrap_g_struct_info_get_methods(PyGIBaseInfo *self)
+{
+    int i, length;
+    PyObject *retval;
+
+    g_base_info_ref(self->info);
+    length = g_struct_info_get_n_methods((GIStructInfo*)self->info);
+    retval = PyTuple_New(length);
+
+    for (i = 0; i < length; i++) {
+        GIFunctionInfo *function;
+	function = g_struct_info_get_method((GIStructInfo*)self->info, i);
+	PyTuple_SetItem(retval, i, pyg_info_new(function));
+	g_base_info_unref((GIBaseInfo*)function);
+    }
+    g_base_info_unref(self->info);
+    
+    return retval;
+}
+
 static PyMethodDef _PyGIStructInfo_methods[] = {
+    { "getMethods", (PyCFunction)_wrap_g_struct_info_get_methods, METH_NOARGS },
     { NULL, NULL, 0 }
 };
 
diff --git a/bank/bank.c b/bank/bank.c
index 7a25f63..202eb7b 100644
--- a/bank/bank.c
+++ b/bank/bank.c
@@ -35,6 +35,7 @@
     REGISTER_TYPE(d, type, name)
 
 static PyMethodDef pybank_functions[] = {
+    //{ "getParamType", (PyCFunction)_wrap_g_type_info_get_param_type, METH_VARARGS },
     { NULL, NULL, 0 }
 };
 
diff --git a/bank/btypes.py b/bank/btypes.py
index 8a8b35b..593bd0a 100644
--- a/bank/btypes.py
+++ b/bank/btypes.py
@@ -120,6 +120,7 @@ class Callable(object):
 
             inArgs.append(value)
 
+        print "invoke with args %r" % inArgs
         retval = self.info.invoke(*inArgs)
 
         if self.info.isConstructor():
@@ -282,3 +283,46 @@ def buildInterface(info):
 
     return newType
 
+def buildBoxed(info):
+    className = info.getName()
+    namespaceName = info.getNamespace()
+    fullName = namespaceName + '.' + className
+
+    if _classDict.has_key(fullName):
+        return _classDict[fullName]
+
+    namespace = {}
+    namespace['__info__'] = info
+    namespace['__module__'] = namespaceName
+    newType = type(className, (object,), namespace)
+
+    constructors = []
+    for method in info.getMethods():
+        if method.isConstructor():
+            constructors.append(method)
+        elif method.isMethod():
+            methodName = method.getName()
+            setattr(newType, methodName, new.instancemethod(Method(method, className),
+                                                            None, newType))
+        else: # probably a static method
+            func = Method(method, className, call_type=Method.STATIC_METHOD)
+            setattr(newType, method.getName(), staticmethod(func))
+
+    setupConstructors(className, newType, constructors)
+
+    def __getattr__(self, name):
+        pass
+    newType.__getattr__ = new.instancemethod(__getattr__, None, newType)
+
+    def __setattr__(self, name, value):
+        pass
+    newType.__setattr__ = new.instancemethod(__setattr__, None, newType)
+
+    def __init__(self, buf):
+        self.__dict__['__buffer__'] = buf
+    newType.__init__ = new.instancemethod(__init__, None, newType)
+
+    _classDict[fullName] = newType
+    
+    return newType
+
diff --git a/bank/module.py b/bank/module.py
index 523504c..32d1986 100644
--- a/bank/module.py
+++ b/bank/module.py
@@ -22,8 +22,9 @@ import os
 import gobject
 from gobject import GEnum
 
-from .btypes import Function, buildClass, buildInterface
-from .repo import EnumInfo, FunctionInfo, ObjectInfo, UnresolvedInfo, InterfaceInfo
+from .btypes import Function, buildClass, buildInterface, buildBoxed
+from .repo import EnumInfo, FunctionInfo, ObjectInfo, UnresolvedInfo, \
+                  InterfaceInfo, StructInfo
 from .repository import repository
 
 class DynamicModule(object):
@@ -85,6 +86,9 @@ class DynamicModule(object):
             return self._create_function(type_info)
         elif isinstance(type_info, InterfaceInfo):
             return self._create_interface(type_info)
+        elif isinstance(type_info, StructInfo) or \
+                isinstance(type_info, BoxedInfo):
+            return self._create_boxed(type_info)
         else:
             raise NotImplementedError(type_info)
 
@@ -181,3 +185,32 @@ class DynamicModule(object):
         self.__dict__[name] = klass
 
         return klass
+
+    def _create_boxed(self, boxed_info):
+        name = boxed_info.getName()
+
+        namespace = repository.get_c_prefix(boxed_info.getNamespace())
+        full_name = namespace + name
+        boxed_info.getGType()
+        gtype = None
+        try:
+            gtype = gobject.GType.from_name(full_name)
+        except RuntimeError:
+            pass
+        else:
+            if gtype.pytype is not None:
+                return gtype.pytype
+        # Check if the klass is already created, eg
+        # present in our namespace, this is necessary since we're
+        # not always entering here through the __getattr__ hook.
+        klass = self.__dict__.get(name)
+        if klass:
+            return klass
+
+        klass = buildBoxed(boxed_info)
+        if gtype is not None:
+            gtype.pytype = klass
+        self.__dict__[name] = klass
+
+        return klass
+
diff --git a/everything_unittest.py b/everything_unittest.py
index 384e590..7be9b55 100644
--- a/everything_unittest.py
+++ b/everything_unittest.py
@@ -242,15 +242,16 @@ class TestGIEverything(unittest.TestCase):
 
     def testStructA(self):
         a = self.createStructA()
-        Everything.test_struct_a_clone(a, a_out)
+        a_out = a.clone()
         self.assertEquals(a, a_out)
 
     def testStructB(self):
         b = Everything.TestStructB()
         b.some_int8 = 3
         b.nested_a = self.createStructA()
-        Everything.test_struct_b_clone(b, b_out)
+        b_out = b.clone()
         self.assertEquals(b, b_out)
+        self.assertEquals(b.nested_a, b_out.nested_a)
 
     def testInterface(self):
         self.assertTrue(issubclass(Everything.TestInterface, gobject.GInterface))



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]