[pygobject] [gi] Register GType for non-GType enums and flags at runtime.
- From: Laszlo Pandy <lpandy src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pygobject] [gi] Register GType for non-GType enums and flags at runtime.
- Date: Tue, 22 Feb 2011 11:18:03 +0000 (UTC)
commit 96f7d1aed732db09a74cd463ed894b7347dbcb15
Author: Laszlo Pandy <lpandy src gnome org>
Date: Sat Feb 19 23:11:25 2011 +0100
[gi] Register GType for non-GType enums and flags at runtime.
Note: rebuild of gobject-introspection is required for new tests.
Previously non-GType enums used a separate type implemented in
Python, and non-GType flags had no implementation at all. This
removes the separate type for enums, and registers a new GType at
runtime if there isn't one.
This allows non-GType enums and flags to use the same Python type
as GType enums and flags. This removes duplication of code, and
make both kinds behave identically.
https://bugzilla.gnome.org/show_bug.cgi?id=642607
gi/gimodule.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++
gi/module.py | 24 +++++++----
gi/pygi-info.c | 15 +++++++
gi/types.py | 16 -------
gobject/pygflags.c | 13 ++++--
tests/test_gi.py | 35 +++++++++++++++
6 files changed, 191 insertions(+), 29 deletions(-)
---
diff --git a/gi/gimodule.c b/gi/gimodule.c
index 55c9d8b..2492668 100644
--- a/gi/gimodule.c
+++ b/gi/gimodule.c
@@ -51,6 +51,63 @@ _wrap_pyg_enum_add (PyObject *self,
}
static PyObject *
+_wrap_pyg_enum_register_new_gtype_and_add (PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "info", NULL };
+ PyGIBaseInfo *py_info;
+ GIEnumInfo *info;
+ gint n_values;
+ GEnumValue *g_enum_values;
+ GType g_type;
+ const gchar *type_name;
+
+ if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+ "O:enum_add_make_new_gtype",
+ kwlist, (PyObject *)&py_info)) {
+ return NULL;
+ }
+
+ if (!GI_IS_ENUM_INFO (py_info->info) ||
+ g_base_info_get_type ((GIBaseInfo *) py_info->info) != GI_INFO_TYPE_ENUM) {
+ PyErr_SetString (PyExc_TypeError, "info must be an EnumInfo with info type GI_INFO_TYPE_ENUM");
+ return NULL;
+ }
+
+ info = (GIEnumInfo *)py_info->info;
+ n_values = g_enum_info_get_n_values (info);
+ g_enum_values = g_new0 (GEnumValue, n_values + 1);
+
+ for (int i=0; i < n_values; i++) {
+ GIValueInfo *value_info;
+ GEnumValue *enum_value;
+ const gchar *name;
+
+ value_info = g_enum_info_get_value (info, i);
+ name = g_base_info_get_name ((GIBaseInfo *) value_info);
+
+ enum_value = &g_enum_values[i];
+ enum_value->value_nick = g_strdup (name);
+ /* TODO: get "c:identifier" attribute for value_name once GI exposes it in the typelib */
+ enum_value->value_name = enum_value->value_nick;
+ enum_value->value = g_value_info_get_value (value_info);
+
+ 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;
+
+ type_name = g_base_info_get_name ((GIBaseInfo *) info);
+ type_name = g_strdup (type_name);
+ g_type = g_enum_register_static (type_name, g_enum_values);
+
+ return pyg_enum_add (NULL, g_type_name (g_type), NULL, g_type);
+}
+
+static PyObject *
_wrap_pyg_flags_add (PyObject *self,
PyObject *args,
PyObject *kwargs)
@@ -74,6 +131,64 @@ _wrap_pyg_flags_add (PyObject *self,
}
static PyObject *
+_wrap_pyg_flags_register_new_gtype_and_add (PyObject *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ static char *kwlist[] = { "info", NULL };
+ PyGIBaseInfo *py_info;
+ GIEnumInfo *info;
+ gint n_values;
+ GFlagsValue *g_flags_values;
+ GType g_type;
+ const gchar *type_name;
+
+ if (!PyArg_ParseTupleAndKeywords (args, kwargs,
+ "O:flags_add_make_new_gtype",
+ kwlist, (PyObject *)&py_info)) {
+ return NULL;
+ }
+
+ if (!GI_IS_ENUM_INFO (py_info->info) ||
+ g_base_info_get_type ((GIBaseInfo *) py_info->info) != GI_INFO_TYPE_FLAGS) {
+ PyErr_SetString (PyExc_TypeError, "info must be an EnumInfo with info type GI_INFO_TYPE_FLAGS");
+ return NULL;
+ }
+
+ info = (GIEnumInfo *)py_info->info;
+ n_values = g_enum_info_get_n_values (info);
+ g_flags_values = g_new0 (GFlagsValue, n_values + 1);
+
+ for (int i=0; i < n_values; i++) {
+ GIValueInfo *value_info;
+ GFlagsValue *flags_value;
+ const gchar *name;
+
+ value_info = g_enum_info_get_value (info, i);
+ name = g_base_info_get_name ((GIBaseInfo *) value_info);
+
+ flags_value = &g_flags_values[i];
+ flags_value->value_nick = g_strdup (name);
+ /* TODO: get "c:identifier" attribute for value_name once GI exposes it in the typelib */
+ flags_value->value_name = flags_value->value_nick;
+ flags_value->value = g_value_info_get_value (value_info);
+
+ 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;
+
+ type_name = g_base_info_get_name ((GIBaseInfo *) info);
+ type_name = g_strdup (type_name);
+ g_type = g_flags_register_static (type_name, g_flags_values);
+
+ return pyg_flags_add (NULL, g_type_name (g_type), NULL, g_type);
+}
+
+
+static PyObject *
_wrap_pyg_set_object_has_new_constructor (PyObject *self,
PyObject *args,
PyObject *kwargs)
@@ -353,7 +468,9 @@ _wrap_pyg_variant_type_from_string (PyObject *self, PyObject *args)
static PyMethodDef _gi_functions[] = {
{ "enum_add", (PyCFunction) _wrap_pyg_enum_add, METH_VARARGS | METH_KEYWORDS },
+ { "enum_register_new_gtype_and_add", (PyCFunction) _wrap_pyg_enum_register_new_gtype_and_add, METH_VARARGS | METH_KEYWORDS },
{ "flags_add", (PyCFunction) _wrap_pyg_flags_add, METH_VARARGS | METH_KEYWORDS },
+ { "flags_register_new_gtype_and_add", (PyCFunction) _wrap_pyg_flags_register_new_gtype_and_add, METH_VARARGS | METH_KEYWORDS },
{ "set_object_has_new_constructor", (PyCFunction) _wrap_pyg_set_object_has_new_constructor, METH_VARARGS | METH_KEYWORDS },
{ "register_interface_info", (PyCFunction) _wrap_pyg_register_interface_info, METH_VARARGS },
diff --git a/gi/module.py b/gi/module.py
index 9b935ed..133f3ed 100644
--- a/gi/module.py
+++ b/gi/module.py
@@ -40,12 +40,13 @@ from ._gi import \
Struct, \
Boxed, \
enum_add, \
- flags_add
+ enum_register_new_gtype_and_add, \
+ flags_add, \
+ flags_register_new_gtype_and_add
from .types import \
GObjectMeta, \
StructMeta, \
- Function, \
- Enum
+ Function
repository = Repository.get_default()
@@ -102,13 +103,18 @@ class IntrospectionModule(object):
wrapper = g_type.pytype
if wrapper is None:
- if g_type.is_a(gobject.TYPE_ENUM):
- wrapper = enum_add(g_type)
- elif g_type.is_a(gobject.TYPE_NONE):
- # An enum with a GType of None is an enum without GType
- wrapper = type(info.get_name(), (Enum,), {})
+ if info.is_flags():
+ if g_type.is_a(gobject.TYPE_FLAGS):
+ wrapper = flags_add(g_type)
+ else:
+ assert g_type == gobject.TYPE_NONE
+ wrapper = flags_register_new_gtype_and_add(info)
else:
- wrapper = flags_add(g_type)
+ if g_type.is_a(gobject.TYPE_ENUM):
+ wrapper = enum_add(g_type)
+ else:
+ assert g_type == gobject.TYPE_NONE
+ wrapper = enum_register_new_gtype_and_add(info)
wrapper.__info__ = info
wrapper.__module__ = 'gi.repository.' + info.get_namespace()
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index f5dd69f..1bfd7d8 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -923,8 +923,23 @@ _wrap_g_enum_info_get_values (PyGIBaseInfo *self)
return infos;
}
+static PyObject *
+_wrap_g_enum_info_is_flags (PyGIBaseInfo *self)
+{
+ GIInfoType info_type = g_base_info_get_type ((GIBaseInfo *) self->info);
+
+ if (info_type == GI_INFO_TYPE_ENUM) {
+ Py_RETURN_FALSE;
+ } else if (info_type == GI_INFO_TYPE_FLAGS) {
+ Py_RETURN_TRUE;
+ } else {
+ g_assert_not_reached();
+ }
+}
+
static PyMethodDef _PyGIEnumInfo_methods[] = {
{ "get_values", (PyCFunction) _wrap_g_enum_info_get_values, METH_NOARGS },
+ { "is_flags", (PyCFunction) _wrap_g_enum_info_is_flags, METH_NOARGS },
{ NULL, NULL, 0 }
};
diff --git a/gi/types.py b/gi/types.py
index 37cf499..c2a8b35 100644
--- a/gi/types.py
+++ b/gi/types.py
@@ -250,19 +250,3 @@ class StructMeta(type, MetaClassHelper):
not method_info.get_arguments():
cls.__new__ = staticmethod(Constructor(method_info))
break
-
-class Enum(int):
- # Only subclasses of this type should be instantiated.
- # Each subclass requires an __info__ attribute,
- # which is not declared here because enums do not share the same gi type.
- def __init__(self, value):
- int.__init__(value)
-
- def __repr__(self):
- value_name = str(self)
- for value_info in self.__info__.get_values():
- if self == value_info.get_value():
- value_name = value_info.get_name().upper()
- return "<enum %s of type %s.%s>" % (value_name,
- self.__info__.get_namespace(),
- self.__info__.get_name())
diff --git a/gobject/pygflags.c b/gobject/pygflags.c
index 936f314..ce99a86 100644
--- a/gobject/pygflags.c
+++ b/gobject/pygflags.c
@@ -172,13 +172,18 @@ pyg_flags_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
pyint = PYGLIB_PyLong_FromLong(value);
ret = PyDict_GetItem(values, pyint);
+ if (!ret) {
+ PyErr_Clear();
+
+ ret = pyg_flags_val_new((PyObject *)type, gtype, pyint);
+ g_assert(ret != NULL);
+ } else {
+ Py_INCREF(ret);
+ }
+
Py_DECREF(pyint);
Py_DECREF(values);
- if (ret)
- Py_INCREF(ret);
- else
- PyErr_Format(PyExc_ValueError, "invalid flag value: %ld", value);
return ret;
}
diff --git a/tests/test_gi.py b/tests/test_gi.py
index 22ff2c4..c12863b 100644
--- a/tests/test_gi.py
+++ b/tests/test_gi.py
@@ -1018,10 +1018,15 @@ class TestGFlags(unittest.TestCase):
self.assertTrue(isinstance(GIMarshallingTests.Flags.VALUE1, GIMarshallingTests.Flags))
self.assertTrue(isinstance(GIMarshallingTests.Flags.VALUE2, GIMarshallingTests.Flags))
self.assertTrue(isinstance(GIMarshallingTests.Flags.VALUE3, GIMarshallingTests.Flags))
+ # __or__() operation should still return an instance, not an int.
+ self.assertTrue(isinstance(GIMarshallingTests.Flags.VALUE1 | GIMarshallingTests.Flags.VALUE2,
+ GIMarshallingTests.Flags))
self.assertEquals(1 << 1, GIMarshallingTests.Flags.VALUE2)
def test_flags_in(self):
GIMarshallingTests.flags_in(GIMarshallingTests.Flags.VALUE2)
+ # result of __or__() operation should still be valid instance, not an int.
+ GIMarshallingTests.flags_in(GIMarshallingTests.Flags.VALUE2 | GIMarshallingTests.Flags.VALUE2)
GIMarshallingTests.flags_in_zero(Number(0))
self.assertRaises(TypeError, GIMarshallingTests.flags_in, 1 << 1)
@@ -1037,6 +1042,36 @@ class TestGFlags(unittest.TestCase):
self.assertTrue(isinstance(flags, GIMarshallingTests.Flags))
self.assertEquals(flags, GIMarshallingTests.Flags.VALUE1)
+class TestNoTypeFlags(unittest.TestCase):
+
+ def test_flags(self):
+ self.assertTrue(issubclass(GIMarshallingTests.NoTypeFlags, GObject.GFlags))
+ self.assertTrue(isinstance(GIMarshallingTests.NoTypeFlags.VALUE1, GIMarshallingTests.NoTypeFlags))
+ self.assertTrue(isinstance(GIMarshallingTests.NoTypeFlags.VALUE2, GIMarshallingTests.NoTypeFlags))
+ self.assertTrue(isinstance(GIMarshallingTests.NoTypeFlags.VALUE3, GIMarshallingTests.NoTypeFlags))
+ # __or__() operation should still return an instance, not an int.
+ self.assertTrue(isinstance(GIMarshallingTests.NoTypeFlags.VALUE1 | GIMarshallingTests.NoTypeFlags.VALUE2,
+ GIMarshallingTests.NoTypeFlags))
+ self.assertEquals(1 << 1, GIMarshallingTests.NoTypeFlags.VALUE2)
+
+ def test_flags_in(self):
+ GIMarshallingTests.no_type_flags_in(GIMarshallingTests.NoTypeFlags.VALUE2)
+ GIMarshallingTests.no_type_flags_in(GIMarshallingTests.NoTypeFlags.VALUE2 | GIMarshallingTests.NoTypeFlags.VALUE2)
+ GIMarshallingTests.no_type_flags_in_zero(Number(0))
+
+ self.assertRaises(TypeError, GIMarshallingTests.no_type_flags_in, 1 << 1)
+ self.assertRaises(TypeError, GIMarshallingTests.no_type_flags_in, 'GIMarshallingTests.NoTypeFlags.VALUE2')
+
+ def test_flags_out(self):
+ flags = GIMarshallingTests.no_type_flags_out()
+ self.assertTrue(isinstance(flags, GIMarshallingTests.NoTypeFlags))
+ self.assertEquals(flags, GIMarshallingTests.NoTypeFlags.VALUE2)
+
+ def test_flags_inout(self):
+ flags = GIMarshallingTests.no_type_flags_inout(GIMarshallingTests.NoTypeFlags.VALUE2)
+ self.assertTrue(isinstance(flags, GIMarshallingTests.NoTypeFlags))
+ self.assertEquals(flags, GIMarshallingTests.NoTypeFlags.VALUE1)
+
class TestStructure(unittest.TestCase):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]