After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 686942 - Failure to override property defined by gi-provided base class
Failure to override property defined by gi-provided base class
Status: RESOLVED FIXED
Product: pygobject
Classification: Bindings
Component: introspection
3.4.x
Other Linux
: Normal normal
: ---
Assigned To: Nobody's working on this now (help wanted and appreciated)
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2012-10-26 13:18 UTC by Simon Schampijer
Modified: 2012-11-06 08:00 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Test case (500 bytes, text/x-python)
2012-10-26 13:18 UTC, Simon Schampijer
  Details
patch (14.37 KB, patch)
2012-10-31 19:16 UTC, Daniel Drake
needs-work Details | Review
patch v2 (18.45 KB, patch)
2012-11-01 15:41 UTC, Daniel Drake
committed Details | Review

Description Simon Schampijer 2012-10-26 13:18:22 UTC
Created attachment 227353 [details]
Test case

Trace from test case which is attached:

{{{
../../test_icon.py:21: Warning: g_value_dup_string: assertion `G_VALUE_HOLDS_STRING (value)' failed
  icon.props.file
}}}

This change got introduced between 3.4.0 and 3.4.1: http://git.gnome.org/browse/pygobject/commit/?h=pygobject-3-4&id=4bfe7972546413f46f5c36737ff03bb5612c1921 introduced it (#684058).
Comment 1 Daniel Drake 2012-10-31 02:41:07 UTC
I guess the root of this problem is that Gtk.Image defines its own file property, as a char*. Then we try to define a new property with the same name and different type, and things go wrong.

Here is the gdb trace:
  • #0 g_logv
    from /lib/libglib-2.0.so.0
  • #1 g_log
    from /lib/libglib-2.0.so.0
  • #2 g_return_if_fail_warning
    from /lib/libglib-2.0.so.0
  • #3 g_value_dup_string
    from /lib/libgobject-2.0.so.0
  • #4 pygi_get_property_value_real
    at pygi-property.c line 166
  • #5 pygi_get_property_value
    at ../../gi/pygi.h line 132
  • #6 PyGProps_getattro
    at pygobject.c line 261
  • #7 PyObject_GetAttr
    from /lib/libpython2.7.so.1.0


Inside pygi_get_property_value_real():

(gdb) p type_tag
$14 = GI_TYPE_TAG_UTF8
(gdb) p value
$15 = {g_type = 135674264, data = {{v_int = 0, v_uint = 0, v_long = 0, 
      v_ulong = 0, v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, 
      v_pointer = 0x0}, {v_int = 0, v_uint = 0, v_long = 0, v_ulong = 0, 
      v_int64 = 0, v_uint64 = 0, v_float = 0, v_double = 0, v_pointer = 0x0}}}
(gdb) p (char *) g_type_name(135674264)
$16 = 0x8164040 "PyObject"


So it tries to call g_value_dup_string() on a PyObject type, and its not happy for obvious reasons.

For the next step, I need to remind myself what would happen in C here, where a subclass defines a property with the same name as one in the parent but with a different type.
Comment 2 Daniel Drake 2012-10-31 19:16:08 UTC
Created attachment 227752 [details] [review]
patch

Here's a patch with full description inside.

The only bit I'm not sure about is what to do with canonicalize_key. What was the purpose of this, what does it solve? I wrote to the mailing list for wider attention.
Comment 3 Simon Feltman 2012-11-01 01:37:08 UTC
Review of attachment 227752 [details] [review]:

This is great and simplifies the internals a bit. In general it seems like a bad idea to override properties at all, are there any warnings that occur when you do this? I just have a few comments and questions. Thanks.

::: gi/_gobject/pygobject.c
@@ +263,1 @@
     pspec = g_object_class_find_property(class, attr_name);

canonicalize_key will need to happen on attr_name or a copy of it before this call. The reason is python properties must use underscores whereas gobject uses hyphens. This will replace underscores with hyphens to make it gobject compatible.

@@ +288,1 @@
+    /* If we reach here, it must be a property defined at the Python level.

I gather "defined at the Python level" means it is a gobject property defined in python (or some other way) and not exposed through gi. In this case it is not necessarily defined in python, just defined dynamically and outside of gi typelibs.

@@ +369,1 @@
     pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(obj), attr_name);

attr_name or a copy of it should be canonicalized before this call.

::: gi/pygi-property.c
@@ +54,2 @@
     repository = g_irepository_get_default();
     info = g_irepository_find_by_gtype (repository, g_type);

Ideally it would be nice if the rest of this function could simply become an API call: g_object_info_find_property
But that's for another time.

@@ +55,3 @@
     info = g_irepository_find_by_gtype (repository, g_type);
+    if (info == NULL || !GI_IS_OBJECT_INFO(info))
+       return NULL;

So now that g_object_class_find_property is being used before the call to pygi_get_property_value within PyGProps_set/getattro, we no longer need to do the recursive lookup here? this is much nicer.

As a side note, if info is not NULL but also not an object info, you need to unref it before returning. Also do you know why is this section of would be entered on a non object type?

::: gi/pygi.h
@@ +78,3 @@
     PyObject* (*type_import_by_g_type) (GType g_type);
     PyObject* (*get_property_value) (PyGObject *instance,
+                                     GParamSpec *pspec);

Please verify the PyGI_API is only used internally by pygi/pygobject. I don't know anything about it other than internals, but if there are clients of it outside of pygobject it would be an API breakage.

::: tests/test_properties.py
@@ +21,3 @@
 from gi.repository import Gio
 from gi.repository import GLib
+from gi.repository import Gtk

Tests dependent on Gtk should live in test_overrides_gtk.py. If possible please use test objects from the Regress or GIMarshellingTests packages here instead of Gtk. Please also verify we have coverage of all possible combinations of property usage and inheritance.

pygobject should attempt independence from gtk as eventually the Gtk overrides and unittests will be moved outside of pygobject, so keeping it separate will make it easier in the future.
Comment 4 Daniel Drake 2012-11-01 15:25:25 UTC
Thanks for the review.

(In reply to comment #3)
> This is great and simplifies the internals a bit. In general it seems like a
> bad idea to override properties at all, are there any warnings that occur when
> you do this? I just have a few comments and questions. Thanks.

No, not here nor in C. Also, the original bug/test case comes from part of a public API that we've been shipping for a while, with many users, so changing it would be a real pain :/
It also doesn't seem like an impossible situation to run into in general, with some standard GTK objects having a large inheritance chain and properties being added to those GTK classes now and then which may conflict with properties defined by existing users on their own subclasses.
 
> @@ +55,3 @@
>      info = g_irepository_find_by_gtype (repository, g_type);
> +    if (info == NULL || !GI_IS_OBJECT_INFO(info))
> +       return NULL;
> 
> So now that g_object_class_find_property is being used before the call to
> pygi_get_property_value within PyGProps_set/getattro, we no longer need to do
> the recursive lookup here? this is much nicer.

Yes - mentioned in the commit message.

> As a side note, if info is not NULL but also not an object info, you need to
> unref it before returning. Also do you know why is this section of would be
> entered on a non object type?

I don't really understand why we didn't hit it before, but we can now arrive here with an interface. This is triggered by test_overrides_gtk.TestGtk.test_viewport: a viewport implements the GtkScrollable interface, and the test adjusts the property owned by the GtkScrollable interface. Without that particular change, the test was crashing.

The next version of the patch adds support for handling GIInterfaceInfo here.

> ::: gi/pygi.h
> @@ +78,3 @@
>      PyObject* (*type_import_by_g_type) (GType g_type);
>      PyObject* (*get_property_value) (PyGObject *instance,
> +                                     GParamSpec *pspec);
> 
> Please verify the PyGI_API is only used internally by pygi/pygobject. I don't
> know anything about it other than internals, but if there are clients of it
> outside of pygobject it would be an API breakage.

I googled around and couldn't find any other users.
 
> ::: tests/test_properties.py
> @@ +21,3 @@
>  from gi.repository import Gio
>  from gi.repository import GLib
> +from gi.repository import Gtk
> 
> Tests dependent on Gtk should live in test_overrides_gtk.py. If possible please
> use test objects from the Regress or GIMarshellingTests packages here instead
> of Gtk. Please also verify we have coverage of all possible combinations of
> property usage and inheritance.

OK, we can use Regress for the same kind of test, and I've also added a few more tests.

I've also addressed all the other comments in the patch coming up.
Comment 5 Daniel Drake 2012-11-01 15:41:14 UTC
Created attachment 227816 [details] [review]
patch v2
Comment 6 Daniel Drake 2012-11-05 15:20:57 UTC
Bump...any chance of a second review here? This is breaking a large part of Sugar. Thanks!
Comment 7 Simon Feltman 2012-11-06 00:19:55 UTC
Review of attachment 227816 [details] [review]:

This looks, good thanks. I still have some reservation about the PyGI_API (mostly because I don't fully understand its usage) and will try to gather more information about it tonight.
Comment 8 Martin Pitt 2012-11-06 08:00:19 UTC
This was discussed in #python, looks all good. Thanks Daniel for fixing this!