GNOME Bugzilla – Bug 686149
fix class initialisation
Last modified: 2016-03-09 17:32:34 UTC
pygobject has some unusual practices for class initialisation. Take a look at pygobject/gobjectmodule.c in pyg_type_register(). There is a rather long comment in that function. Here are some highlights: * Note: Interfaces to be implemented are searched twice. First * we register interfaces that are already implemented by a parent * type. The second time, the remaining interfaces are * registered, i.e. the ones that are not implemented by a parent * type. In between these two loops, properties and signals are * registered. It has to be done this way, in two steps, * otherwise glib will complain. * This looks like a GLib quirk, but no bug has been filed * upstream. In practice this ends up looking like: type = g_type_register_static(...); class = g_type_class_ref (type); add_some_interfaces (type); add_some_properties (class); add_some_signals (type); add_some_more_interfaces (type); Note in particular that interfaces are added after g_type_class_ref() is called. g_type_class_ref() must be called to get the GObjectClass so that properties can be added which (as by the comment above) is necessarily done before adding some of the interfaces. This complicated approach is caused by the fact that pygobject does type initialisation in a way that no other (known) user of gobject does. Most users (effectively) do this: type = g_type_register_static(...); g_type_add_interface_static(type, ...); return type; ... wait for someone to call g_type_class_ref(type) ... _class_init() { g_signal_new(...); g_object_class_install_property(...); ... } When done in this order there are no 'quirks' to work around. A goal in GObject is to prevent interfaces from being added to types after g_type_class_ref() has been called on the type. Removing the possibility of the interfaces that a class implements from changing at runtime will allow us to reduce the complexity of the code for dispatching interface calls and improve the performance. I considered making the rule "you cannot add interfaces after a class has been instantiated at least once" but this turns out to be somewhat more complicated. A new state flag ("has ever been instantiated") would have to be added to the type info structure in order for us to check this and doing so would be slightly non-trivial. g_type_class_ref() already changes a lot of state so it's easy to see if it's ever been called -- and it's a logical choice because it is *usually* called only as part of type instantiation or by subclasses (another point at which it is too late to add interfaces, really).
Thanks for this detailled description! From that it was rather straightforward to make the necessary changes, I spent most of the time on sorting out some data type, GLib warning, and test issues.
Created attachment 227969 [details] [review] Move property and signal creation into _class_init() This patch changes class initialization to the order you suggested, which is indeed a lot more straightforward and avoids the double interface initialization hack. I tested this on Python 3.3.0 and 2.7.3, and the whole test suite passes with both. I'd appreciate a second pair of eyes on that for a sanity check, as well as your confirmation that this now does what you mean. Thanks!
Applying the GLib patch in bug 687659 causes the pygobject testsuite to fail like so: (process:22791): GLib-GObject-WARNING **: attempting to add an interface (TestInterface) to class (test_interface+MyObject) after class_init Trace/breakpoint trap After your patch it's working again.
I confirm comment 3, I tried the same thing in jhbuild. Pushed.