[pygi] Implement nullable argument support, including tests



commit 79aa416ae8632b123da61d79fb820d9e2704209c
Author: Zach Goldberg <zach zachgoldberg com>
Date:   Sat Apr 17 12:00:05 2010 -0400

    Implement nullable argument support, including tests
    
    https://bugzilla.gnome.org/show_bug.cgi?id=616035

 gi/pygi-argument.c       |   43 +++++++++++++++++++++++++++++++++++++------
 gi/pygi-argument.h       |    3 ++-
 gi/pygi-info.c           |   19 +++++++++++++------
 tests/test_everything.py |   28 ++++++++++++++++++++++++++++
 4 files changed, 80 insertions(+), 13 deletions(-)
---
diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index 0737bb7..df88a6c 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -189,11 +189,16 @@ _pygi_g_registered_type_info_check_object (GIRegisteredTypeInfo *info,
 
 gint
 _pygi_g_type_info_check_object (GITypeInfo *type_info,
-                                PyObject   *object)
+                                PyObject   *object,
+                                gboolean   allow_none)
 {
     GITypeTag type_tag;
     gint retval = 1;
 
+    if (allow_none && object == Py_None) {
+        return retval;
+    }
+
     type_tag = g_type_info_get_tag(type_info);
 
     switch (type_tag) {
@@ -351,7 +356,7 @@ check_number_release:
                     break;
                 }
 
-                retval = _pygi_g_type_info_check_object(item_type_info, item);
+                retval = _pygi_g_type_info_check_object(item_type_info, item, TRUE);
 
                 Py_DECREF(item);
 
@@ -481,7 +486,7 @@ check_number_release:
                     break;
                 }
 
-                retval = _pygi_g_type_info_check_object(item_type_info, item);
+                retval = _pygi_g_type_info_check_object(item_type_info, item, TRUE);
 
                 Py_DECREF(item);
 
@@ -545,7 +550,7 @@ check_number_release:
                 key = PyList_GET_ITEM(keys, i);
                 value = PyList_GET_ITEM(values, i);
 
-                retval = _pygi_g_type_info_check_object(key_type_info, key);
+                retval = _pygi_g_type_info_check_object(key_type_info, key, TRUE);
                 if (retval < 0) {
                     break;
                 }
@@ -554,7 +559,7 @@ check_number_release:
                     break;
                 }
 
-                retval = _pygi_g_type_info_check_object(value_type_info, value);
+                retval = _pygi_g_type_info_check_object(value_type_info, value, TRUE);
                 if (retval < 0) {
                     break;
                 }
@@ -591,6 +596,10 @@ _pygi_argument_to_array (GArgument  *arg,
     gssize length;
     GArray *g_array;
 
+    if (arg->v_pointer == NULL) {
+        return NULL;
+    }
+ 
     is_zero_terminated = g_type_info_is_zero_terminated(type_info);
     item_type_info = g_type_info_get_param_type(type_info, 0);
 
@@ -788,6 +797,11 @@ _pygi_argument_from_object (PyObject   *object,
         {
             const gchar *string;
 
+            if (object == Py_None){
+                arg.v_string = NULL;
+                break;
+            }
+
             string = PyString_AsString(object);
 
             /* Don't need to check for errors, since g_strdup is NULL-proof. */
@@ -822,6 +836,11 @@ _pygi_argument_from_object (PyObject   *object,
             GITransfer item_transfer;
             Py_ssize_t i;
 
+            if (object == Py_None){
+                arg.v_pointer = NULL;
+                break;
+            }
+
             length = PySequence_Length(object);
             if (length < 0) {
                 break;
@@ -1005,6 +1024,11 @@ array_item_error:
             GITransfer item_transfer;
             Py_ssize_t i;
 
+            if (object == Py_None) {
+                arg.v_pointer = NULL;
+                break;
+            }
+
             length = PySequence_Length(object);
             if (length < 0) {
                 break;
@@ -1071,6 +1095,11 @@ list_item_error:
             Py_ssize_t i;
 
 
+            if (object == Py_None){
+                arg.v_pointer = NULL;                
+                break;
+            }
+
             length = PyMapping_Length(object);
             if (length < 0) {
                 break;
@@ -1661,7 +1690,9 @@ _pygi_argument_release (GArgument   *arg,
             break;
         case GI_TYPE_TAG_FILENAME:
         case GI_TYPE_TAG_UTF8:
-            if ((direction == GI_DIRECTION_IN && transfer == GI_TRANSFER_NOTHING)
+            /* With allow-none support the string could be NULL */
+            if (arg->v_string != NULL && 
+                (direction == GI_DIRECTION_IN && transfer == GI_TRANSFER_NOTHING)
                     || (direction == GI_DIRECTION_OUT && transfer == GI_TRANSFER_EVERYTHING)) {
                 g_free(arg->v_string);
             }
diff --git a/gi/pygi-argument.h b/gi/pygi-argument.h
index c162f2f..821737a 100644
--- a/gi/pygi-argument.h
+++ b/gi/pygi-argument.h
@@ -32,7 +32,8 @@ G_BEGIN_DECLS
 /* Private */
 
 gint _pygi_g_type_info_check_object (GITypeInfo *type_info,
-                                     PyObject   *object);
+                                     PyObject   *object,
+                                     gboolean   allow_none);
 
 gint _pygi_g_registered_type_info_check_object (GIRegisteredTypeInfo *info,
                                                 gboolean              is_instance,
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index dbcf999..824e579 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -692,6 +692,7 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self,
             GITypeTag type_tag;
             PyObject *py_arg;
             gint retval;
+            gboolean allow_none;
 
             direction = g_arg_info_get_direction(arg_infos[i]);
             type_tag = g_type_info_get_tag(arg_type_infos[i]);
@@ -705,7 +706,11 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self,
             g_assert(py_args_pos < n_py_args);
             py_arg = PyTuple_GET_ITEM(py_args, py_args_pos);
 
-            retval = _pygi_g_type_info_check_object(arg_type_infos[i], py_arg);
+            allow_none = g_arg_info_may_be_null(arg_infos[i]);
+
+            retval = _pygi_g_type_info_check_object(arg_type_infos[i],
+                                                    py_arg,
+                                                    allow_none);
 
             if (retval < 0) {
                 goto out;
@@ -945,11 +950,13 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self,
                     }
 
                     /* Get rid of the GArray. */
-                    args[i]->v_pointer = array->data;
+                    if (array != NULL) {
+                        args[i]->v_pointer = array->data;
 
-                    if (direction != GI_DIRECTION_INOUT || transfer != GI_TRANSFER_NOTHING) {
-                        /* The array hasn't been referenced anywhere, so free it to avoid losing memory. */
-                        g_array_free(array, FALSE);
+                        if (direction != GI_DIRECTION_INOUT || transfer != GI_TRANSFER_NOTHING) {
+                            /* The array hasn't been referenced anywhere, so free it to avoid losing memory. */
+                            g_array_free(array, FALSE);
+                        }
                     }
                 }
 
@@ -1990,7 +1997,7 @@ _wrap_g_field_info_set_value (PyGIBaseInfo *self,
     {
         gboolean retval;
 
-        retval = _pygi_g_type_info_check_object(field_type_info, py_value);
+        retval = _pygi_g_type_info_check_object(field_type_info, py_value, TRUE);
         if (retval < 0) {
             goto out;
         }
diff --git a/tests/test_everything.py b/tests/test_everything.py
index 8347081..148b8af 100644
--- a/tests/test_everything.py
+++ b/tests/test_everything.py
@@ -46,6 +46,34 @@ class TestEverything(unittest.TestCase):
         self.assertEquals(surface.get_width(), 10)
         self.assertEquals(surface.get_height(), 10)
 
+
+class TestNullableArgs(unittest.TestCase):
+    def test_in_nullable_hash(self):
+        Everything.test_ghash_null_in(None)
+
+    def test_in_nullable_list(self):
+        Everything.test_gslist_null_in(None)
+        Everything.test_glist_null_in(None)
+
+    def test_in_nullable_array(self):
+        Everything.test_array_int_null_in(None, -1)
+
+    def test_in_nullable_string(self):
+        Everything.test_utf8_null_in(None)
+
+    def test_out_nullable_hash(self):
+        self.assertEqual(None, Everything.test_ghash_null_out())
+
+    def test_out_nullable_list(self):
+        self.assertEqual(None, Everything.test_gslist_null_out())
+        self.assertEqual(None, Everything.test_glist_null_out())
+
+    def test_out_nullable_array(self):
+        self.assertEqual((None, 0), Everything.test_array_int_null_out())
+
+    def test_out_nullable_string(self):
+        self.assertEqual(None, Everything.test_utf8_null_out())
+
 class TestCallbacks(unittest.TestCase):
     called = False
     def testCallback(self):



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