[pygobject/gsoc2009: 20/160] Add array support in function calls from Python



commit 1311524ee6a0fcf4e52143f152fb3ee9c325ddc3
Author: Simon van der Linden <svdlinden src gnome org>
Date:   Fri Jul 10 17:21:48 2009 +0200

    Add array support in function calls from Python
    
    Complete checks in pyg_argument_from_pyobject_check.
    Add pyg_array_from_pyobject and refactore pyg_array_to_pyobject.
    Add auxiliary argument support in _wrap_g_function_info_invoke.
    
    Add most test_array_* and test_strv_* from Everything.

 girepository/bank-argument.c |  206 ++++++++++++++++++++++++++++++-------
 girepository/bank-info.c     |  234 ++++++++++++++++++++++++++++++++++--------
 girepository/bank.h          |    7 +-
 tests/test_girepository.py   |  107 ++++++++++++++------
 4 files changed, 441 insertions(+), 113 deletions(-)
---
diff --git a/girepository/bank-argument.c b/girepository/bank-argument.c
index 9f4d058..1939e11 100644
--- a/girepository/bank-argument.c
+++ b/girepository/bank-argument.c
@@ -20,6 +20,7 @@
 
 #include "bank.h"
 #include <pygobject.h>
+#include <string.h>
 
 GQuark
 pyg_argument_from_pyobject_error_quark(void)
@@ -234,7 +235,11 @@ pyg_argument_from_pyobject_check(PyObject *object, GITypeInfo *type_info, GError
             break;
         case GI_TYPE_TAG_ARRAY:
         {
-            gint size;
+            gssize size;
+            gssize required_size;
+            Py_ssize_t object_size;
+            GITypeInfo *item_type_info;
+            gsize i;
 
             if (!PyTuple_Check(object)) {
                 py_type_name_expected = "tuple";
@@ -242,14 +247,32 @@ pyg_argument_from_pyobject_check(PyObject *object, GITypeInfo *type_info, GError
             }
 
             size = g_type_info_get_array_fixed_size(type_info);
-            if (size != -1 && PyTuple_Size(object) != size) {
-                /* FIXME: Set another more appropriate error. */
-                g_set_error(error, PyG_ARGUMENT_FROM_PYOBJECT_ERROR, PyG_ARGUMENT_FROM_PYOBJECT_ERROR_VALUE, "FIXME");
+            required_size = g_type_info_get_array_fixed_size(type_info);
+            object_size = PyTuple_Size(object);
+            if (required_size != -1 && object_size != required_size) {
+                g_set_error(error, PyG_ARGUMENT_FROM_PYOBJECT_ERROR, PyG_ARGUMENT_FROM_PYOBJECT_ERROR_SIZE,
+                        "Must contain %zd items, not %zd", required_size, object_size);
                 retval = FALSE;
                 break;
             }
 
-            /* TODO: to complete */
+            item_type_info = g_type_info_get_param_type(type_info, 0);
+
+            for (i = 0; i < object_size; i++) {
+                PyObject *item;
+
+                item = PyTuple_GetItem(object, i);
+                g_assert(item != NULL);
+
+                g_assert(error == NULL || *error == NULL);
+                if (!pyg_argument_from_pyobject_check(item, item_type_info, error)) {
+                    g_prefix_error(error, "Item %zu: ", i);
+                    retval = FALSE;
+                    break;
+                }
+            }
+
+            g_base_info_unref((GIBaseInfo *)item_type_info);
 
             break;
         }
@@ -413,8 +436,11 @@ pyg_argument_from_pyobject(PyObject *object, GITypeInfo *type_info)
             arg.v_pointer = pygobject_get(object);
         break;
     case GI_TYPE_TAG_ARRAY:
-        arg.v_pointer = NULL;
+    {
+        gsize length;
+        arg.v_pointer = pyg_array_from_pyobject(object, type_info, &length);
         break;
+    }
     case GI_TYPE_TAG_ERROR:
         /* Allow NULL GError, otherwise fall through */
         if (object == Py_None) {
@@ -483,51 +509,159 @@ glist_to_pyobject(GITypeTag list_tag, GITypeInfo *type_info, GList *list, GSList
     return py_list;
 }
 
-PyObject *
-pyarray_to_pyobject(gpointer array, int length, GITypeInfo *type_info)
+static
+gsize
+pyg_type_get_size(GITypeTag type_tag)
 {
-    PyObject *py_list;
-    PyObject *child_obj;
-    GITypeInfo *element_type = g_type_info_get_param_type (type_info, 0);
-    GITypeTag type_tag = g_type_info_get_tag(element_type);
     gsize size;
-    char buf[256];
-    int i;
 
-    if (array == NULL)
-        return Py_None;
+    switch(type_tag) {
+        case GI_TYPE_TAG_VOID:
+            size = sizeof(void);
+            break;
+        case GI_TYPE_TAG_BOOLEAN:
+            size = sizeof(gboolean);
+            break;
+        case GI_TYPE_TAG_INT:
+        case GI_TYPE_TAG_UINT:
+            size = sizeof(gint);
+            break;
+        case GI_TYPE_TAG_INT8:
+        case GI_TYPE_TAG_UINT8:
+            size = sizeof(gint8);
+            break;
+        case GI_TYPE_TAG_INT16:
+        case GI_TYPE_TAG_UINT16:
+            size = sizeof(gint16);
+            break;
+        case GI_TYPE_TAG_INT32:
+        case GI_TYPE_TAG_UINT32:
+            size = sizeof(gint32);
+            break;
+        case GI_TYPE_TAG_INT64:
+        case GI_TYPE_TAG_UINT64:
+            size = sizeof(gint64);
+            break;
+        case GI_TYPE_TAG_LONG:
+        case GI_TYPE_TAG_ULONG:
+            size = sizeof(glong);
+            break;
+        case GI_TYPE_TAG_SIZE:
+        case GI_TYPE_TAG_SSIZE:
+            size = sizeof(gsize);
+            break;
+        case GI_TYPE_TAG_FLOAT:
+            size = sizeof(gfloat);
+            break;
+        case GI_TYPE_TAG_DOUBLE:
+            size = sizeof(gdouble);
+            break;
+        case GI_TYPE_TAG_UTF8:
+            size = sizeof(gchar *);
+            break;
+        default:
+            /* TODO: Complete with other types */
+            g_assert_not_reached();
+    }
+
+    return size;
+}
 
-    // FIXME: Doesn't seem right to have this here:
-    switch (type_tag) {
-    case GI_TYPE_TAG_INT:
-        size = sizeof(int);
-        break;
-    case GI_TYPE_TAG_INTERFACE:
-        size = sizeof(gpointer);
-        break;
-    default:
-        snprintf(buf, sizeof(buf), "Unimplemented type: %s\n", g_type_tag_to_string(type_tag));
-        PyErr_SetString(PyExc_TypeError, buf);
+gpointer
+pyg_array_from_pyobject(PyObject *object, GITypeInfo *type_info, gsize *length)
+{
+    gpointer items;
+    gpointer current_item;
+    gssize item_size;
+    gboolean is_zero_terminated;
+    GITypeInfo *item_type_info;
+    GITypeTag type_tag;
+    gsize i;
+
+    is_zero_terminated = g_type_info_is_zero_terminated(type_info);
+    item_type_info = g_type_info_get_param_type(type_info, 0);
+
+    type_tag = g_type_info_get_tag(item_type_info);
+    item_size = pyg_type_get_size(type_tag);
+
+    *length = PyTuple_Size(object);
+    items = g_try_malloc(*length * (item_size + (is_zero_terminated ? 1 : 0)));
+
+    if (items == NULL) {
+        g_base_info_unref((GIBaseInfo *)item_type_info);
         return NULL;
     }
 
-    if ((py_list = PyList_New(0)) == NULL) {
+    current_item = items;
+    for (i = 0; i < *length; i++) {
+        GArgument arg;
+        PyObject *py_item;
+
+        py_item = PyTuple_GetItem(object, i);
+        g_assert(py_item != NULL);
+
+        arg = pyg_argument_from_pyobject(py_item, item_type_info);
+
+        g_memmove(current_item, &arg, item_size);
+
+        current_item += item_size;
+    }
+
+    if (is_zero_terminated) {
+        memset(current_item, 0, item_size);
+    }
+
+    g_base_info_unref((GIBaseInfo *)item_type_info);
+
+    return items;
+}
+
+PyObject *
+pyg_array_to_pyobject(gpointer items, gsize length, GITypeInfo *type_info)
+{
+    PyObject *py_items;
+    gsize item_size;
+    GITypeInfo *item_type_info;
+    GITypeTag type_tag;
+    gsize i;
+    gpointer current_item;
+
+    if (g_type_info_is_zero_terminated(type_info)) {
+        length = g_strv_length(items);
+    }
+
+    py_items = PyTuple_New(length);
+    if (py_items == NULL) {
         return NULL;
     }
 
-    for( i = 0; i < length; i++ ) {
-        gpointer current_element = array + i * size;
+    item_type_info = g_type_info_get_param_type (type_info, 0);
+    type_tag = g_type_info_get_tag(item_type_info);
+    item_size = pyg_type_get_size(type_tag);
+
+    current_item = items;
+    for(i = 0; i < length; i++) {
+        PyObject *item;
+        int retval;
 
-        child_obj = pyg_argument_to_pyobject((GArgument *)&current_element, element_type);
-        if (child_obj == NULL) {
-            Py_DECREF(py_list);
+        item = pyg_argument_to_pyobject((GArgument *)current_item, item_type_info);
+        if (item == NULL) {
+            g_base_info_unref((GIBaseInfo *)item_type_info);
+            Py_DECREF(py_items);
             return NULL;
         }
-        PyList_Append(py_list, child_obj);
-        Py_DECREF(child_obj);
+
+        retval = PyTuple_SetItem(py_items, i, item);
+        if (retval) {
+            g_base_info_unref((GIBaseInfo *)item_type_info);
+            Py_DECREF(py_items);
+            return NULL;
+        }
+
+        current_item += item_size;
     }
 
-    return py_list;
+    return py_items;
 }
 
 PyObject *
diff --git a/girepository/bank-info.c b/girepository/bank-info.c
index 652b1f8..ed1246c 100644
--- a/girepository/bank-info.c
+++ b/girepository/bank-info.c
@@ -335,6 +335,8 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
     guint n_in_args;
     guint n_out_args;
     Py_ssize_t n_py_args;
+    gsize n_aux_in_args;
+    gsize n_aux_out_args;
     guint n_return_values;
 
     GICallableInfo *callable_info;
@@ -343,6 +345,7 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
 
     GArgument *in_args;
     GArgument *out_args;
+    GArgument **aux_args;
     GArgument *out_values;
     GArgument return_arg;
     PyObject *return_value;
@@ -364,14 +367,23 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
     n_py_args = PyTuple_Size(args);
     n_in_args = is_method ? 1 : 0;  /* The first argument is the instance. */
     n_out_args = 0;
+    n_aux_in_args = 0;
+    n_aux_out_args = 0;
+    aux_args = g_newa(GArgument *, n_args);
 
     for (i = 0; i < n_args; i++) {
         GIDirection direction;
         GIArgInfo *arg_info;
+        GITypeInfo *arg_type_info;
+        GITypeTag arg_type_tag;
 
         arg_info = g_callable_info_get_arg(callable_info, i);
+
+        arg_type_info = g_arg_info_get_type(arg_info);
         direction = g_arg_info_get_direction(arg_info);
 
+        arg_type_tag = g_type_info_get_tag(arg_type_info);
+
         if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
             n_in_args += 1;
         }
@@ -379,18 +391,53 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
             n_out_args += 1;
         }
 
+        aux_args[i] = NULL;
+        if (arg_type_tag == GI_TYPE_TAG_ARRAY) {
+            gint length_arg_pos;
+
+            length_arg_pos = g_type_info_get_array_length(arg_type_info);
+            if (length_arg_pos != -1) {
+                /* Tag the argument as auxiliary. Later, it'll be changed into a pointer to (in|out)_args[(in|out)_args_pos].
+                 * We cannot do it now since we don't know how much space to allocate (n_(in|out)_args) yet.
+                 */
+                aux_args[length_arg_pos] = (GArgument *)!NULL;
+
+                if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
+                    n_aux_in_args += 1;
+                }
+                if (direction == GI_DIRECTION_OUT || direction == GI_DIRECTION_INOUT) {
+                    n_aux_out_args += 1;
+                }
+            }
+        }
+
+        g_base_info_unref((GIBaseInfo *)arg_type_info);
         g_base_info_unref((GIBaseInfo *)arg_info);
     }
 
+    return_info = g_callable_info_get_return_type((GICallableInfo *)self->info);
+    return_tag = g_type_info_get_tag(return_info);
+
+    /* Tag the return value's auxiliary argument too, if applicable. */
+    if (return_tag == GI_TYPE_TAG_ARRAY) {
+        gint length_arg_pos;
+        length_arg_pos = g_type_info_get_array_length(return_info);
+        if (length_arg_pos != -1) {
+            aux_args[length_arg_pos] = (GArgument *)!NULL;
+            n_aux_out_args += 1;
+        }
+    }
+
     {
         Py_ssize_t py_args_pos;
 
         /* Check the argument count. */
-        if (n_py_args != n_in_args + (is_constructor ? 1 : 0)) {
-            PyErr_Format(PyExc_TypeError, "%s.%s() takes exactly %i argument(s) (%zd given)",
+        if (n_py_args != n_in_args + (is_constructor ? 1 : 0) - n_aux_in_args) {
+            g_base_info_unref((GIBaseInfo *)return_info);
+            PyErr_Format(PyExc_TypeError, "%s.%s() takes exactly %zd argument(s) (%zd given)",
                     g_base_info_get_namespace((GIBaseInfo *)callable_info),
                     g_base_info_get_name((GIBaseInfo *)callable_info),
-                    n_in_args + (is_constructor ? 1 : 0), n_py_args);
+                    n_in_args + (is_constructor ? 1 : 0) - n_aux_in_args, n_py_args);
             return NULL;
         }
 
@@ -404,6 +451,11 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
             PyObject *py_arg;
             GError *error;
 
+            if (aux_args[i] != NULL) {
+                /* No check needed for auxiliary arguments. */
+                continue;
+            }
+
             arg_info = g_callable_info_get_arg(callable_info, i);
             direction = g_arg_info_get_direction(arg_info);
             if (!(direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT)) {
@@ -424,6 +476,7 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
                     case PyG_ARGUMENT_FROM_PYOBJECT_ERROR_TYPE:
                         py_error_type = PyExc_TypeError;
                         break;
+                    case PyG_ARGUMENT_FROM_PYOBJECT_ERROR_SIZE:
                     case PyG_ARGUMENT_FROM_PYOBJECT_ERROR_VALUE:
                         py_error_type = PyExc_ValueError;
                         break;
@@ -444,6 +497,7 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
             g_base_info_unref((GIBaseInfo *)arg_info);
 
             if (PyErr_Occurred()) {
+                g_base_info_unref((GIBaseInfo *)return_info);
                 return NULL;
             }
 
@@ -453,7 +507,6 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
         g_assert(py_args_pos == n_py_args);
     }
 
-    /* Get the arguments. */
     in_args = g_newa(GArgument, n_in_args);
     out_args = g_newa(GArgument, n_out_args);
     /* each out arg is a pointer, they point to these values */
@@ -461,6 +514,46 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
        http://bugzilla.gnome.org/show_bug.cgi?id=573314 */
     out_values = g_newa(GArgument, n_out_args);
 
+    /* Link aux_args to in_args. */
+    {
+        gsize in_args_pos;
+        gsize out_args_pos;
+
+        in_args_pos = is_method ? 1 : 0;
+        out_args_pos = 0;
+
+        for (i = 0; i < n_args; i++) {
+            GIArgInfo *arg_info;
+            GIDirection direction;
+
+            arg_info = g_callable_info_get_arg((GICallableInfo *)self->info, i);
+            direction = g_arg_info_get_direction(arg_info);
+
+            if (direction == GI_DIRECTION_IN) {
+                if (aux_args[i] != NULL) {
+                    g_assert(in_args_pos < n_in_args);
+                    aux_args[i] = &in_args[in_args_pos];
+                }
+                in_args_pos += 1;
+            } else {
+                if (aux_args[i] != NULL) {
+                    g_assert(out_args_pos < n_out_args);
+                    aux_args[i] = &out_values[out_args_pos];
+                }
+                out_args_pos += 1;
+                if (direction == GI_DIRECTION_INOUT) {
+                    in_args_pos += 1;
+                }
+            }
+
+            g_base_info_unref((GIBaseInfo *)arg_info);
+        }
+
+        g_assert(in_args_pos == n_in_args);
+        g_assert(out_args_pos == n_out_args);
+    }
+
+    /* Get the arguments. */
     {
         guint in_args_pos = 0;
         guint out_args_pos = 0;
@@ -486,6 +579,7 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
                 py_buffer = PyObject_GetAttrString((PyObject *)py_arg, "__buffer__");
                 if (py_buffer == NULL) {
                     g_base_info_unref(container);
+                    g_base_info_unref((GIBaseInfo *)return_info);
                     return NULL;
                 }
                 py_buffer_procs = py_buffer->ob_type->tp_as_buffer;
@@ -521,6 +615,23 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
 
             if (direction == GI_DIRECTION_IN || direction == GI_DIRECTION_INOUT) {
                 PyObject *py_arg;
+                GITypeInfo *arg_type_info;
+                GITypeTag arg_type_tag;
+
+                g_assert(in_args_pos < n_in_args);
+                if (direction == GI_DIRECTION_INOUT) {
+                    in_args[in_args_pos].v_pointer = out_value;
+                }
+
+                if (aux_args[i] != NULL) {
+                    /* Auxiliary argument has already been set or will be set later. */
+                    in_args_pos += 1;
+                    g_base_info_unref((GIBaseInfo *)arg_info);
+                    continue;
+                }
+
+                arg_type_info = g_arg_info_get_type(arg_info);
+                arg_type_tag = g_type_info_get_tag(arg_type_info);
 
                 g_assert(py_args_pos < n_py_args);
                 py_arg = PyTuple_GetItem(args, py_args_pos);
@@ -528,7 +639,6 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
 
                 GArgument in_value = pyg_argument_from_pyobject(py_arg, g_arg_info_get_type(arg_info));
 
-                g_assert(in_args_pos < n_in_args);
                 if (direction == GI_DIRECTION_IN) {
                     /* Pass the value. */
                     in_args[in_args_pos] = in_value;
@@ -536,9 +646,27 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
                     /* Pass a pointer to the value. */
                     g_assert(out_value != NULL);
                     *out_value = in_value;
-                    in_args[in_args_pos].v_pointer = out_value;
                 }
 
+                if (arg_type_tag == GI_TYPE_TAG_ARRAY) {
+                    gint length_arg_pos;
+                    length_arg_pos = g_type_info_get_array_length(arg_type_info);
+                    if (length_arg_pos != -1) {
+                        GArgument *length_arg;
+                        Py_ssize_t size;
+
+                        size = PyTuple_Size(py_arg);
+                        if (size > 0) {
+                            g_assert(in_value.v_pointer != NULL);
+                        }
+                        length_arg = aux_args[length_arg_pos];
+                        g_assert(length_arg != NULL);
+                        length_arg->v_size = size;
+                    }
+                }
+
+                g_base_info_unref((GIBaseInfo *)arg_type_info);
+
                 in_args_pos += 1;
                 py_args_pos += 1;
             }
@@ -553,14 +681,16 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
 
     /* Invoke the callable. */
     {
-        GError *error = NULL;
+        GError *error;
+
+        error = NULL;
 
         if (!g_function_info_invoke((GIFunctionInfo *)callable_info,
                     in_args, n_in_args,
                     out_args, n_out_args,
                     &return_arg,
                     &error)) {
-            g_assert(error != NULL);
+            g_base_info_unref((GIBaseInfo *)return_info);
             PyErr_Format(PyExc_RuntimeError, "Error invoking %s.%s(): %s",
                     g_base_info_get_namespace((GIBaseInfo *)callable_info),
                     g_base_info_get_name((GIBaseInfo *)callable_info),
@@ -570,17 +700,35 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
         }
     }
 
-    /* Get return value. */
-    return_info = g_callable_info_get_return_type(callable_info);
-    return_tag = g_type_info_get_tag(return_info);
-
-    n_return_values = n_out_args;
+    n_return_values = n_out_args - n_aux_out_args;
     if (return_tag != GI_TYPE_TAG_VOID) {
         n_return_values += 1;
 
         if (!is_constructor) {
             /* Convert the return value. */
-            return_value = pyg_argument_to_pyobject(&return_arg, return_info);
+            if (return_tag == GI_TYPE_TAG_ARRAY) {
+                gssize length;
+                length = g_type_info_get_array_fixed_size(return_info);
+                if (length < 0) {
+                    gint length_arg_pos;
+                    GArgument *length_arg;
+
+                    length_arg_pos = g_type_info_get_array_length(return_info);
+
+                    if (length_arg_pos >= 0) {
+                        length_arg = aux_args[length_arg_pos];
+                        g_assert(length_arg != NULL);
+                        length = length_arg->v_int;
+                    }
+                }
+                if (length < 0) {
+                    g_assert(g_type_info_is_zero_terminated(return_info));
+                }
+                return_value = pyg_array_to_pyobject(return_arg.v_pointer, length, return_info);
+            } else {
+                return_value = pyg_argument_to_pyobject(&return_arg, return_info);
+            }
+
             g_assert(return_value != NULL);
         } else {
             /* Wrap the return value inside the first argument. */
@@ -692,57 +840,57 @@ _wrap_g_function_info_invoke(PyGIBaseInfo *self, PyObject *args)
             } else {
                 PyObject *obj;
                 GITypeTag type_tag;
-                int retval;
+                GArgument *arg;
 
                 if (direction == GI_DIRECTION_INOUT) {
                     g_assert(in_args_pos < n_in_args);
                 }
                 g_assert(out_args_pos < n_out_args);
 
+                if (aux_args[i] != NULL) {
+                    if (direction == GI_DIRECTION_INOUT) {
+                        in_args_pos += 1;
+                    }
+                    out_args_pos += 1;
+                    g_base_info_unref((GIBaseInfo *)arg_type_info);
+                    g_base_info_unref((GIBaseInfo *)arg_info);
+                    continue;
+                }
+
+                arg = (GArgument *)out_args[out_args_pos].v_pointer;
+
                 type_tag = g_type_info_get_tag(arg_type_info);
 
                 if (type_tag == GI_TYPE_TAG_ARRAY) {
-                    /* FIXME: Verify all that... */
+                    gssize length;
 
-                    gint length_arg_index;
-                    GArgument *length_arg;
-                    GArgument *arg;
+                    length = g_type_info_get_array_fixed_size(arg_type_info);
+                    if (length < 0) {
+                        gint length_arg_pos;
+                        GArgument *length_arg;
 
-                    length_arg_index = g_type_info_get_array_length(arg_type_info);
+                        length_arg_pos = g_type_info_get_array_length(arg_type_info);
 
-                    if (is_method) {
-                        /* XXX: Why? */
-                        length_arg_index -= 1;
+                        if (length_arg_pos >= 0) {
+                            length_arg = aux_args[length_arg_pos];
+                            g_assert(length_arg != NULL);
+                            length = length_arg->v_int;
+                        }
                     }
-
-                    if (length_arg_index == -1) {
-                        g_base_info_unref((GIBaseInfo *)arg_type_info);
-                        g_base_info_unref((GIBaseInfo *)arg_info);
-                        g_base_info_unref((GIBaseInfo *)return_info);
-                        PyErr_SetString(PyExc_NotImplementedError, "Need a field to specify the array length");
-                        return NULL;
+                    if (length < 0) {
+                        g_assert(g_type_info_is_zero_terminated(arg_type_info));
                     }
 
-                    length_arg = out_args[length_arg_index].v_pointer;
-
-                    if (length_arg == NULL) {
-                        g_base_info_unref((GIBaseInfo *)arg_type_info);
-                        g_base_info_unref((GIBaseInfo *)arg_info);
-                        g_base_info_unref((GIBaseInfo *)return_info);
-                        PyErr_SetString(PyExc_RuntimeError, "Failed to get the length of the array");
-                        return NULL;
-                    }
-
-                    arg = (GArgument *)out_args[out_args_pos].v_pointer;
-                    obj = pyarray_to_pyobject(arg->v_pointer, length_arg->v_int, arg_type_info);
+                    obj = pyg_array_to_pyobject(arg->v_pointer, length, arg_type_info);
                 } else {
-                    obj = pyg_argument_to_pyobject(out_args[out_args_pos].v_pointer, arg_type_info);
+                    obj = pyg_argument_to_pyobject(arg, arg_type_info);
                 }
 
                 g_assert(obj != NULL);
                 g_assert(return_values_pos < n_return_values);
 
                 if (n_return_values > 1) {
+                    int retval;
                     g_assert(return_value != NULL);
 
                     retval = PyTuple_SetItem(return_value, return_values_pos, obj);
diff --git a/girepository/bank.h b/girepository/bank.h
index 3f76b77..5808f80 100644
--- a/girepository/bank.h
+++ b/girepository/bank.h
@@ -76,14 +76,17 @@ GArgument pyg_argument_from_pyobject(PyObject *object,
 				     GITypeInfo *info);
 PyObject*  pyg_argument_to_pyobject(GArgument *arg,
 				    GITypeInfo *info);
-PyObject*  pyarray_to_pyobject(gpointer array, int length, GITypeInfo *info);
+
+PyObject* pyg_array_to_pyobject(gpointer items, gsize length, GITypeInfo *info);
+gpointer pyg_array_from_pyobject(PyObject *object, GITypeInfo *type_info, gsize *length);
 
 #define PyG_ARGUMENT_FROM_PYOBJECT_ERROR pyg_argument_from_pyobject_error_quark()
 GQuark pyg_argument_from_pyobject_error_quark(void);
 
 typedef enum {
     PyG_ARGUMENT_FROM_PYOBJECT_ERROR_TYPE,
-    PyG_ARGUMENT_FROM_PYOBJECT_ERROR_VALUE
+    PyG_ARGUMENT_FROM_PYOBJECT_ERROR_VALUE,
+    PyG_ARGUMENT_FROM_PYOBJECT_ERROR_SIZE
 } PyGArgumentFromPyObjectError;
 
 gboolean pyg_argument_from_pyobject_check(PyObject *object, GITypeInfo *type_info, GError **error);
diff --git a/tests/test_girepository.py b/tests/test_girepository.py
index a07f16a..4e08c11 100644
--- a/tests/test_girepository.py
+++ b/tests/test_girepository.py
@@ -260,23 +260,81 @@ class TestGIEverything(unittest.TestCase):
         self.assertEquals(("first", "second"), Everything.test_utf8_out_nonconst_return())
 
 
-# FIXME
-# ======================================================================
-# ERROR: testStrv (__main__.TestGIEverything)
-# ----------------------------------------------------------------------
-# Traceback (most recent call last):
-#   File "test_girepository.py", line 179, in testStrv
-#     self.assertTrue(Everything.test_strv_in(('1', '2', '3')))
-#   File "/opt/gnome-introspection/lib64/python2.5/site-packages/gtk-2.0/girepository/btypes.py", line 124, in __call__
-#     self.type_check(name, value, argType)
-#   File "/opt/gnome-introspection/lib64/python2.5/site-packages/gtk-2.0/girepository/btypes.py", line 89, in type_check
-#     raise TypeError("Must pass None for arrays currently")
-# TypeError: Must pass None for arrays currently
-#    def testStrv(self):
-#        self.assertTrue(Everything.test_strv_in(('1', '2', '3')))
-#        self.assertTrue(Everything.test_strv_in(['1', '2', '3'])) # XXX valid?
-#        self.assertRaises(TypeError, Everything.test_strv_in(('1', 2, 3)))
-#        self.assertEquals((1, 2, 3), Everything.test_strv_out())
+# Arrays
+
+    def testArrayIntIn(self):
+        self.assertEquals(5, Everything.test_array_int_in((1, 2, 3, -1)))
+
+        self.assertRaises(TypeError, Everything.test_array_int_in, 0)
+        self.assertRaises(TypeError, Everything.test_array_int_in, (2, 'a'))
+
+    def testArrayInt8In(self):
+        self.assertEquals(5, Everything.test_array_gint8_in((1, 2, 3, -1)))
+        self.assertEquals(-1, Everything.test_array_gint8_in((INT8_MAX, INT8_MIN)))
+
+        self.assertRaises(TypeError, Everything.test_array_gint8_in, 0)
+        self.assertRaises(TypeError, Everything.test_array_gint8_in, (2, 'a'))
+        self.assertRaises(ValueError, Everything.test_array_gint8_in, (INT8_MAX+1,))
+        self.assertRaises(ValueError, Everything.test_array_gint8_in, (INT8_MIN-1,))
+
+    def testArrayInt16In(self):
+        self.assertEquals(5, Everything.test_array_gint16_in((1, 2, 3, -1)))
+        self.assertEquals(-1, Everything.test_array_gint16_in((INT16_MAX, INT16_MIN)))
+
+        self.assertRaises(TypeError, Everything.test_array_gint16_in, 0)
+        self.assertRaises(TypeError, Everything.test_array_gint16_in, (2, 'a'))
+        self.assertRaises(ValueError, Everything.test_array_gint16_in, (INT16_MAX+1,))
+        self.assertRaises(ValueError, Everything.test_array_gint16_in, (INT16_MIN-1,))
+
+    def testArrayInt32In(self):
+        self.assertEquals(5, Everything.test_array_gint32_in((1, 2, 3, -1)))
+        self.assertEquals(-1, Everything.test_array_gint32_in((INT32_MAX, INT32_MIN)))
+
+        self.assertRaises(TypeError, Everything.test_array_gint32_in, 0)
+        self.assertRaises(TypeError, Everything.test_array_gint32_in, (2, 'a'))
+        self.assertRaises(ValueError, Everything.test_array_gint32_in, (INT32_MAX+1,))
+        self.assertRaises(ValueError, Everything.test_array_gint32_in, (INT32_MIN-1,))
+
+    def testArrayInt32In(self):
+        self.assertEquals(5, Everything.test_array_gint32_in((1, 2, 3, -1)))
+        self.assertEquals(-1, Everything.test_array_gint32_in((INT32_MAX, INT32_MIN)))
+
+        self.assertRaises(TypeError, Everything.test_array_gint32_in, 0)
+        self.assertRaises(TypeError, Everything.test_array_gint32_in, (2, 'a'))
+        self.assertRaises(ValueError, Everything.test_array_gint32_in, (INT32_MAX+1,))
+        self.assertRaises(ValueError, Everything.test_array_gint32_in, (INT32_MIN-1,))
+
+    def testArrayInt64In(self):
+        self.assertEquals(5, Everything.test_array_gint64_in((1, 2, 3, -1)))
+        self.assertEquals(-1, Everything.test_array_gint64_in((INT64_MAX, INT64_MIN)))
+
+        self.assertRaises(TypeError, Everything.test_array_gint64_in, 0)
+        self.assertRaises(TypeError, Everything.test_array_gint64_in, (2, 'a'))
+        self.assertRaises(ValueError, Everything.test_array_gint64_in, (INT64_MAX+1,))
+        self.assertRaises(ValueError, Everything.test_array_gint64_in, (INT64_MIN-1,))
+
+    def testStrvIn(self):
+        self.assertTrue(Everything.test_strv_in(('1', '2', '3')))
+        self.assertFalse(Everything.test_strv_in(('1', '2')))
+        self.assertFalse(Everything.test_strv_in(('1', '2', '3', '4')))
+
+        self.assertRaises(TypeError, Everything.test_strv_in, '1')
+        self.assertRaises(TypeError, Everything.test_strv_in, ('1', 2, 3))
+
+    def testArrayGTypeIn(self):
+        # TODO
+        pass
+
+    def testStrvOut(self):
+        self.assertEquals(("thanks", "for", "all", "the", "fish"), Everything.test_strv_out())
+        self.assertEquals(("thanks", "for", "all", "the", "fish"), Everything.test_strv_out_c())
+
+    def testStrvOutarg(self):
+        self.assertEquals(("1", "2", "3"), Everything.test_strv_outarg())
+
+
+# Interface
+# GSList
 
     def testGList(self):
         retval = Everything.test_glist_nothing_return()
@@ -456,21 +514,6 @@ class TestGIEverything(unittest.TestCase):
         s = TestSubclass()
         self.assertEquals(s.do_matrix('matrix'), 42)
 
-# FIXME
-# ======================================================================
-# ERROR: testArrayOut (__main__.TestGIEverything)
-# ----------------------------------------------------------------------
-# Traceback (most recent call last):
-#   File "test_girepository.py", line 282, in testArrayOut
-# 	b, n_ints, ints = Everything.test_array_int_full_out2()
-#   File "/opt/gnome-introspection/lib64/python2.5/site-packages/gtk-2.0/girepository/module.py", line 56, in __getattr__
-# 	self.__class__.__name__, name))
-# AttributeError: 'DynamicModule' object has no attribute 'test_array_int_full_out2'
-#	def testArrayOut(self):
-#	    b, n_ints, ints = Everything.test_array_int_full_out2()
-#	    self.assertEquals(b, True)
-#	    self.assertEquals(n_ints, 5)
-#	    self.assertEquals(ints, [1, 2, 3, 4, 5])
 
 if __name__ == '__main__':
     suite = unittest.TestLoader().loadTestsFromTestCase(TestGIEverything)



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