[pygobject] Unify property getters



commit b0236d6fde137e0b2ecf7f5556ad5d53c22874bc
Author: Simon Feltman <sfeltman src gnome org>
Date:   Fri Aug 8 20:55:28 2014 -0700

    Unify property getters
    
    Consolidate duplicate logic into pygi_get_property_value().
    Use the function for GObject.get_property(), GObject.get_properties(),
    and GObject.props.
    Remove overridden expected failures in TestCGetPropertyMethod which
    now work due to the unification.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=733893
    https://bugzilla.gnome.org/show_bug.cgi?id=726999

 gi/pygi-property.c       |   85 ++++++++++++++++++++++++++++------------
 gi/pygi-property.h       |    4 ++
 gi/pygobject.c           |   97 ++++++---------------------------------------
 tests/test_properties.py |   38 ------------------
 4 files changed, 77 insertions(+), 147 deletions(-)
---
diff --git a/gi/pygi-property.c b/gi/pygi-property.c
index 65fc325..9a6e0ea 100644
--- a/gi/pygi-property.c
+++ b/gi/pygi-property.c
@@ -101,47 +101,82 @@ pygi_get_property_value (PyGObject *instance, GParamSpec *pspec)
 {
     GIPropertyInfo *property_info = NULL;
     GValue value = { 0, };
-    GIArgument arg = { 0, };
     PyObject *py_value = NULL;
-    GITypeInfo *type_info = NULL;
-    gboolean free_array = FALSE;
 
-    /* The owner_type of the pspec gives us the exact type that introduced the
-     * property, even if it is a parent class of the instance in question. */
-    property_info = _pygi_lookup_property_from_g_type (pspec->owner_type, pspec->name);
-
-    if (property_info == NULL)
-        goto out;
+    if (!(pspec->flags & G_PARAM_READABLE)) {
+        PyErr_Format(PyExc_TypeError, "property %s is not readable",
+                     g_param_spec_get_name (pspec));
+        return NULL;
+    }
 
+    Py_BEGIN_ALLOW_THREADS;
     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
     g_object_get_property (instance->obj, pspec->name, &value);
+    Py_END_ALLOW_THREADS;
 
-    type_info = g_property_info_get_type (property_info);
-    arg = _pygi_argument_from_g_value (&value, type_info);
-
-    /* Arrays are special cased, see note in _pygi_argument_to_array. */
-    if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ARRAY) {
-        arg.v_pointer = _pygi_argument_to_array (&arg, NULL, NULL, NULL,
-                                                 type_info, &free_array);
+    /* Fast path which checks if this is a Python implemented Property.
+     * TODO: Make it even faster by calling the Python getter implementation
+     * directly. See: https://bugzilla.gnome.org/show_bug.cgi?id=723872 */
+    if (pyg_gtype_is_custom (pspec->owner_type)) {
+        py_value = pyg_param_gvalue_as_pyobject (&value, TRUE, pspec);
+        goto out;
     }
 
-    py_value = _pygi_argument_to_object (&arg, type_info, GI_TRANSFER_NOTHING);
+    /* Attempt to marshal through GI.
+     * The owner_type of the pspec gives us the exact type that introduced the
+     * property, even if it is a parent class of the instance in question. */
+    property_info = _pygi_lookup_property_from_g_type (pspec->owner_type, pspec->name);
+    if (property_info) {
+        GITypeInfo *type_info = NULL;
+        gboolean free_array = FALSE;
+        GIArgument arg = { 0, };
+
+        type_info = g_property_info_get_type (property_info);
+        arg = _pygi_argument_from_g_value (&value, type_info);
+
+        /* Arrays are special cased, see note in _pygi_argument_to_array. */
+        if (g_type_info_get_tag (type_info) == GI_TYPE_TAG_ARRAY) {
+            arg.v_pointer = _pygi_argument_to_array (&arg, NULL, NULL, NULL,
+                                                     type_info, &free_array);
+        }
 
-    if (free_array) {
-        g_array_free (arg.v_pointer, FALSE);
-    }
+        py_value = _pygi_argument_to_object (&arg, type_info, GI_TRANSFER_NOTHING);
 
-    g_value_unset (&value);
+        if (free_array) {
+            g_array_free (arg.v_pointer, FALSE);
+        }
 
-out:
-    if (property_info != NULL)
-        g_base_info_unref (property_info);
-    if (type_info != NULL)
         g_base_info_unref (type_info);
+        g_base_info_unref (property_info);
+    }
+
+    /* Fallback to GValue marshalling. */
+    if (py_value == NULL) {
+        py_value = pyg_param_gvalue_as_pyobject (&value, TRUE, pspec);
+    }
 
+out:
+    g_value_unset (&value);
     return py_value;
 }
 
+PyObject *
+pygi_get_property_value_by_name (PyGObject *self, gchar *param_name)
+{
+    GParamSpec *pspec;
+
+    pspec = g_object_class_find_property (G_OBJECT_GET_CLASS(self->obj),
+                                          param_name);
+    if (!pspec) {
+        PyErr_Format (PyExc_TypeError,
+                      "object of type `%s' does not have property `%s'",
+                      g_type_name (G_OBJECT_TYPE (self->obj)), param_name);
+        return NULL;
+    }
+
+    return pygi_get_property_value (self, pspec);
+}
+
 gint
 pygi_set_property_value (PyGObject *instance,
                          GParamSpec *pspec,
diff --git a/gi/pygi-property.h b/gi/pygi-property.h
index 5678bc3..a08fa40 100644
--- a/gi/pygi-property.h
+++ b/gi/pygi-property.h
@@ -33,6 +33,10 @@ PyObject *
 pygi_get_property_value (PyGObject *instance,
                          GParamSpec *pspec);
 
+PyObject *
+pygi_get_property_value_by_name (PyGObject *self,
+                                 gchar *param_name);
+
 gint
 pygi_set_property_value (PyGObject *instance,
                          GParamSpec *pspec,
diff --git a/gi/pygobject.c b/gi/pygobject.c
index d006c33..83e460a 100644
--- a/gi/pygobject.c
+++ b/gi/pygobject.c
@@ -248,15 +248,12 @@ build_parameter_list(GObjectClass *class)
     return props_list;
 }
 
-
 static PyObject*
 PyGProps_getattro(PyGProps *self, PyObject *attr)
 {
     char *attr_name, *property_name;
     GObjectClass *class;
     GParamSpec *pspec;
-    GValue value = { 0, };
-    PyObject *ret;
 
     attr_name = PYGLIB_PyUnicode_AsString(attr);
     if (!attr_name) {
@@ -279,36 +276,12 @@ PyGProps_getattro(PyGProps *self, PyObject *attr)
        return PyObject_GenericGetAttr((PyObject *)self, attr);
     }
 
-    if (!(pspec->flags & G_PARAM_READABLE)) {
-       PyErr_Format(PyExc_TypeError,
-                    "property '%s' is not readable", attr_name);
-       return NULL;
-    }
-
     if (!self->pygobject) {
         /* If we're doing it without an instance, return a GParamSpec */
         return pyg_param_spec_new(pspec);
     }
 
-    if (!pyg_gtype_is_custom (pspec->owner_type)) {
-        /* The GType is not implemented at the Python level: see if we can
-         * read the property value via gi. */
-        ret = pygi_get_property_value (self->pygobject, pspec);
-        if (ret)
-            return ret;
-    }
-
-    /* The GType is implemented in Python, or we failed to read it via gi:
-     * do a straightforward read. */
-    Py_BEGIN_ALLOW_THREADS;
-    g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
-    g_object_get_property(self->pygobject->obj, pspec->name, &value);
-    Py_END_ALLOW_THREADS;
-
-    ret = pyg_param_gvalue_as_pyobject(&value, TRUE, pspec);
-    g_value_unset(&value);
-    
-    return ret;
+    return pygi_get_property_value (self->pygobject, pspec);
 }
 
 static gboolean
@@ -1323,45 +1296,22 @@ pygobject_init(PyGObject *self, PyObject *args, PyObject *kwargs)
     }
 
 static PyObject *
-pygobject_get_property(PyGObject *self, PyObject *args)
+pygobject_get_property (PyGObject *self, PyObject *args)
 {
     gchar *param_name;
-    GParamSpec *pspec;
-    GValue value = { 0, };
-    PyObject *ret;
 
-    if (!PyArg_ParseTuple(args, "s:GObject.get_property", &param_name))
-       return NULL;
+    if (!PyArg_ParseTuple (args, "s:GObject.get_property", &param_name)) {
+        return NULL;
+    }
 
     CHECK_GOBJECT(self);
-    
-    pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(self->obj),
-                                        param_name);
-    if (!pspec) {
-       PyErr_Format(PyExc_TypeError,
-                    "object of type `%s' does not have property `%s'",
-                    g_type_name(G_OBJECT_TYPE(self->obj)), param_name);
-       return NULL;
-    }
-    if (!(pspec->flags & G_PARAM_READABLE)) {
-       PyErr_Format(PyExc_TypeError, "property %s is not readable",
-                    param_name);
-       return NULL;
-    }
-    g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
-    Py_BEGIN_ALLOW_THREADS;
-    g_object_get_property(self->obj, param_name, &value);
-    Py_END_ALLOW_THREADS;
 
-    ret = pyg_param_gvalue_as_pyobject(&value, TRUE, pspec);
-    g_value_unset(&value);
-    return ret;
+    return pygi_get_property_value_by_name (self, param_name);
 }
 
 static PyObject *
 pygobject_get_properties(PyGObject *self, PyObject *args)
 {
-    GObjectClass *class;
     int len, i;
     PyObject *tuple;
 
@@ -1371,48 +1321,27 @@ pygobject_get_properties(PyGObject *self, PyObject *args)
     }
 
     tuple = PyTuple_New(len);
-    class = G_OBJECT_GET_CLASS(self->obj);
     for (i = 0; i < len; i++) {
         PyObject *py_property = PyTuple_GetItem(args, i);
         gchar *property_name;
-        GParamSpec *pspec;
-        GValue value = { 0 };
         PyObject *item;
 
         if (!PYGLIB_PyUnicode_Check(py_property)) {
             PyErr_SetString(PyExc_TypeError,
                             "Expected string argument for property.");
-            return NULL;
+            goto fail;
         }
 
         property_name = PYGLIB_PyUnicode_AsString(py_property);
-
-        pspec = g_object_class_find_property(class,
-                                        property_name);
-        if (!pspec) {
-           PyErr_Format(PyExc_TypeError,
-                        "object of type `%s' does not have property `%s'",
-                        g_type_name(G_OBJECT_TYPE(self->obj)), property_name);
-       return NULL;
-        }
-        if (!(pspec->flags & G_PARAM_READABLE)) {
-           PyErr_Format(PyExc_TypeError, "property %s is not readable",
-                       property_name);
-           return NULL;
-        }
-        g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
-
-        Py_BEGIN_ALLOW_THREADS;
-        g_object_get_property(self->obj, property_name, &value);
-        Py_END_ALLOW_THREADS;
-
-        item = pyg_value_as_pyobject(&value, TRUE);
-        PyTuple_SetItem(tuple, i, item);
-
-        g_value_unset(&value);
+        item = pygi_get_property_value_by_name (self, property_name);
+        PyTuple_SetItem (tuple, i, item);
     }
 
     return tuple;
+
+fail:
+    Py_DECREF (tuple);
+    return NULL;
 }
 
 static PyObject *
diff --git a/tests/test_properties.py b/tests/test_properties.py
index 9138361..999bff1 100644
--- a/tests/test_properties.py
+++ b/tests/test_properties.py
@@ -1280,44 +1280,6 @@ class TestCGetPropertyMethod(CPropertiesTestBase, unittest.TestCase):
     def set_prop(self, obj, name, value):
         obj.set_property(name, value)
 
-    @unittest.expectedFailure  # https://bugzilla.gnome.org/show_bug.cgi?id=733893
-    def test_boxed_glist(self):
-        # get_property() returns None or a GIMarshallingTestsBoxedGList
-        CPropertiesTestBase.test_boxed_glist(self)
-
-    @unittest.expectedFailure  # https://bugzilla.gnome.org/show_bug.cgi?id=733893
-    def test_annotated_glist(self):
-        # get_property() returns None
-        CPropertiesTestBase.test_annotated_glist(self)
-
-    @unittest.expectedFailure  # https://bugzilla.gnome.org/show_bug.cgi?id=733893
-    def test_char(self):
-        # get_property() returns a single element string which cannot represent
-        # tested values for G_TYPE_CHAR
-        CPropertiesTestBase.test_char(self)
-
-    @unittest.expectedFailure  # https://bugzilla.gnome.org/show_bug.cgi?id=733893
-    def test_uchar(self):
-        # get_property() returns a single element string which cannot represent
-        # tested values for G_TYPE_UCHAR
-        CPropertiesTestBase.test_uchar(self)
-
-    @unittest.expectedFailure  # https://bugzilla.gnome.org/show_bug.cgi?id=733893
-    def test_setting_several_properties(self):
-        # get_property() returns a single element string which cannot represent
-        # tested values for G_TYPE_UCHAR
-        CPropertiesTestBase.test_setting_several_properties(self)
-
-    @unittest.expectedFailure  # https://bugzilla.gnome.org/show_bug.cgi?id=733893
-    def test_parent_class(self):
-        # get_property() returns gpointer instead of a list
-        CPropertiesTestBase.test_annotated_glist(self)
-
-    @unittest.expectedFailure  # https://bugzilla.gnome.org/show_bug.cgi?id=733893
-    def test_hash_table(self):
-        # get_property() returns gpointer instead of a dict
-        CPropertiesTestBase.test_hash_table(self)
-
 
 if __name__ == '__main__':
     unittest.main()


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