[pygobject] Change dynamic enum and flag gtype creation to use namespaced naming
- From: Simon Feltman <sfeltman src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject] Change dynamic enum and flag gtype creation to use namespaced naming
- Date: Tue, 1 Jan 2013 20:47:40 +0000 (UTC)
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]