[pygobject] Change dynamic enum and flag gtype creation to use namespaced naming



commit 6c02ab0ad720780f176192fdc6372aaa178812fd
Author: Simon Feltman <sfeltman src gnome org>
Date:   Mon Dec 31 02:53:07 2012 -0800

    Change dynamic enum and flag gtype creation to use namespaced naming
    
    Use the combination of g_base_info_get_namespace and g_base_info_get_name
    as the name for registering enum and flag types with glib through
    g_enum_register_static and g_flags_register_static. This avoids conflicts
    with types like GLib.SeekType and Gst.SeekType. Add better exceptions
    and memory cleanup for invalid registration problems.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=690455

 gi/_gobject/pygenum.c |    6 ++--
 gi/gimodule.c         |   78 ++++++++++++++++++++++++++++++++++++++++++------
 tests/test_gi.py      |   35 ++++++++++++++++++++++
 3 files changed, 106 insertions(+), 13 deletions(-)
---
diff --git a/gi/_gobject/pygenum.c b/gi/_gobject/pygenum.c
index 9c3c455..89e3a06 100644
--- a/gi/_gobject/pygenum.c
+++ b/gi/_gobject/pygenum.c
@@ -216,9 +216,9 @@ pyg_enum_add (PyObject *   module,
     int i;
 
     g_return_val_if_fail(typename != NULL, NULL);
-    if (!g_type_is_a(gtype, G_TYPE_ENUM)) {
-        g_warning("Trying to register gtype '%s' as enum when in fact it is of type '%s'",
-                  g_type_name(gtype), g_type_name(G_TYPE_FUNDAMENTAL(gtype)));
+    if (!g_type_is_a (gtype, G_TYPE_ENUM)) {
+        PyErr_Format (PyExc_TypeError, "Trying to register gtype '%s' as enum when in fact it is of type '%s'",
+                      g_type_name (gtype), g_type_name (G_TYPE_FUNDAMENTAL (gtype)));
         return NULL;
     }
 
diff --git a/gi/gimodule.c b/gi/gimodule.c
index f5c0026..8246d29 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -62,7 +62,9 @@ _wrap_pyg_enum_register_new_gtype_and_add (PyObject *self,
     gint n_values;
     GEnumValue *g_enum_values;
     int i;
+    const gchar *namespace;
     const gchar *type_name;
+    gchar *full_name;
     GType g_type;
 
     if (!PyArg_ParseTupleAndKeywords (args, kwargs,
@@ -79,6 +81,10 @@ _wrap_pyg_enum_register_new_gtype_and_add (PyObject *self,
 
     info = (GIEnumInfo *)py_info->info;
     n_values = g_enum_info_get_n_values (info);
+
+    /* The new memory is zero filled which fulfills the registration
+     * function requirement that the last item is zeroed out as a terminator.
+     */
     g_enum_values = g_new0 (GEnumValue, n_values + 1);
 
     for (i = 0; i < n_values; i++) {
@@ -105,13 +111,36 @@ _wrap_pyg_enum_register_new_gtype_and_add (PyObject *self,
         g_base_info_unref ((GIBaseInfo *) value_info);
     }
 
-    g_enum_values[n_values].value = 0;
-    g_enum_values[n_values].value_nick = NULL;
-    g_enum_values[n_values].value_name = NULL;
-
+    namespace = g_base_info_get_namespace ((GIBaseInfo *) info);
     type_name = g_base_info_get_name ((GIBaseInfo *) info);
-    g_type = g_enum_register_static (type_name, g_enum_values);
+    full_name = g_strconcat (namespace, type_name, NULL);
+
+    /* If enum registration fails, free all the memory allocated
+     * for the values array. This needs to leak when successful
+     * as GObject keeps a reference to the data as specified in the docs.
+     */
+    g_type = g_enum_register_static (full_name, g_enum_values);
+    if (g_type == G_TYPE_INVALID) {
+        for (i = 0; i < n_values; i++) {
+            GEnumValue *enum_value = &g_enum_values[i];
+
+            /* Only free value_name if it is different from value_nick to avoid
+             * a double free. The pointer might have been is re-used in the case
+             * c_identifier was NULL in the above loop.
+             */
+            if (enum_value->value_name != enum_value->value_nick)
+                g_free ((gchar *) enum_value->value_name);
+            g_free ((gchar *) enum_value->value_nick);
+        }
+
+        g_free (g_enum_values);
+        g_free (full_name);
+
+        PyErr_Format (PyExc_RuntimeError, "Unable to register enum '%s'", full_name);
+        return NULL;
+    }
 
+    g_free (full_name);
     return pyg_enum_add (NULL, g_type_name (g_type), NULL, g_type);
 }
 
@@ -149,7 +178,9 @@ _wrap_pyg_flags_register_new_gtype_and_add (PyObject *self,
     gint n_values;
     GFlagsValue *g_flags_values;
     int i;
+    const gchar *namespace;
     const gchar *type_name;
+    gchar *full_name;
     GType g_type;
 
     if (!PyArg_ParseTupleAndKeywords (args, kwargs,
@@ -166,6 +197,10 @@ _wrap_pyg_flags_register_new_gtype_and_add (PyObject *self,
 
     info = (GIEnumInfo *)py_info->info;
     n_values = g_enum_info_get_n_values (info);
+
+    /* The new memory is zero filled which fulfills the registration
+     * function requirement that the last item is zeroed out as a terminator.
+     */
     g_flags_values = g_new0 (GFlagsValue, n_values + 1);
 
     for (i = 0; i < n_values; i++) {
@@ -192,13 +227,36 @@ _wrap_pyg_flags_register_new_gtype_and_add (PyObject *self,
         g_base_info_unref ((GIBaseInfo *) value_info);
     }
 
-    g_flags_values[n_values].value = 0;
-    g_flags_values[n_values].value_nick = NULL;
-    g_flags_values[n_values].value_name = NULL;
-
+    namespace = g_base_info_get_namespace ((GIBaseInfo *) info);
     type_name = g_base_info_get_name ((GIBaseInfo *) info);
-    g_type = g_flags_register_static (type_name, g_flags_values);
+    full_name = g_strconcat (namespace, type_name, NULL);
+
+    /* If enum registration fails, free all the memory allocated
+     * for the values array. This needs to leak when successful
+     * as GObject keeps a reference to the data as specified in the docs.
+     */
+    g_type = g_flags_register_static (full_name, g_flags_values);
+    if (g_type == G_TYPE_INVALID) {
+        for (i = 0; i < n_values; i++) {
+            GFlagsValue *flags_value = &g_flags_values[i];
+
+            /* Only free value_name if it is different from value_nick to avoid
+             * a double free. The pointer might have been is re-used in the case
+             * c_identifier was NULL in the above loop.
+             */
+            if (flags_value->value_name != flags_value->value_nick)
+                g_free ((gchar *) flags_value->value_name);
+            g_free ((gchar *) flags_value->value_nick);
+        }
+
+        g_free (g_flags_values);
+        g_free (full_name);
+
+        PyErr_Format (PyExc_RuntimeError, "Unable to register flags '%s'", full_name);
+        return NULL;
+    }
 
+    g_free (full_name);
     return pyg_flags_add (NULL, g_type_name (g_type), NULL, g_type);
 }
 
diff --git a/tests/test_gi.py b/tests/test_gi.py
index 0e161d7..7a0fd80 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -1489,6 +1489,26 @@ class TestEnum(unittest.TestCase):
         self.assertTrue(hasattr(GIMarshallingTests.Enum, "VALUE1"))
         self.assertRaises(AttributeError, getattr, GIMarshallingTests.SecondEnum, "VALUE1")
 
+    def test_enum_gtype_name_is_namespaced(self):
+        self.assertEqual(GIMarshallingTests.Enum.__gtype__.name,
+                         'GIMarshallingTestsEnum')
+
+    def test_enum_double_registration_error(self):
+        # a warning is printed for double registration and pygobject will
+        # also raise a RuntimeError.
+        old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_ERROR)
+        try:
+            self.assertRaises(RuntimeError,
+                              gi._gi.enum_register_new_gtype_and_add,
+                              GIMarshallingTests.Enum.__info__)
+        finally:
+            GLib.log_set_always_fatal(old_mask)
+
+    def test_enum_add_type_error(self):
+        self.assertRaises(TypeError,
+                          gi._gi.enum_add,
+                          GIMarshallingTests.NoTypeFlags.__gtype__)
+
 
 class TestGEnum(unittest.TestCase):
 
@@ -1621,6 +1641,21 @@ class TestNoTypeFlags(unittest.TestCase):
         self.assertTrue(isinstance(flags, GIMarshallingTests.NoTypeFlags))
         self.assertEqual(flags, GIMarshallingTests.NoTypeFlags.VALUE1)
 
+    def test_flags_gtype_name_is_namespaced(self):
+        self.assertEqual(GIMarshallingTests.NoTypeFlags.__gtype__.name,
+                         'GIMarshallingTestsNoTypeFlags')
+
+    def test_flags_double_registration_error(self):
+        # a warning is printed for double registration and pygobject will
+        # also raise a RuntimeError.
+        old_mask = GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_ERROR)
+        try:
+            self.assertRaises(RuntimeError,
+                              gi._gi.flags_register_new_gtype_and_add,
+                              GIMarshallingTests.NoTypeFlags.__info__)
+        finally:
+            GLib.log_set_always_fatal(old_mask)
+
 
 class TestStructure(unittest.TestCase):
 



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