GNOME Bugzilla – Bug 123891
gobject.PARAM_CONSTRUCT problem
Last modified: 2006-01-09 14:11:28 UTC
When creating a GObject property with the gobject.PARAM_CONSTRUCT flag the gobject.GObject.__init__(self) calls the do_set_property() method with that property initial value. The problem is that the self object used in that do_set_property() call during the initialization is different that the one returned by the call to MyObject(). Run the example attached to understand what I mean
Created attachment 20475 [details] code example
Just for reference, this is the sequence of events that happens when you do instantiate an object that calls __gobject_init__/GObject.__init__ in its constructor, and has custom properties that get initialised: [ foo is the instance, FooBar is the class below, baz is a construct property ] Python code constructs instance (w = FooBar()) * FooBar.__init__ is called * FooBar.__init__ calls self.__gobject_init__() * __gobject_init__ calls g_object_new(typeid, ...) * instance is created * set_property called for all construct properties ===> * do_set_property called on PyObject wrapper corresponding to newly created GObject. * __gobject_init__ assigns new instance to "obj" member of PyGObject struct for self * associate "self" with the new GObject instance w now instantiated. The problem is that the wrapper isn't associated with the C level instance until g_object_new() returns. However, setting of properties may try to request a wrapper for the new C level instance (which creates one if none exist). I am not sure how to fix this. I can check to see if a wrapper has been created when g_object_new() returns, but I can't really tell the do_set_property wrapper about the wrapper that is being constructed.
Can you perhaps delay the call to do_set_wrapper until the 'real' instance is available?
By do_set_wrapper, do you mean do_set_property()? If so, then the answer is no. The set_property calls get initiated by the code in libgobject, rather than anything I have control of. The definition of a construct property is that set_property() will always get invoked for the property during g_object_new(). If the programmer doesn't specify a value for the property, set_property() gets called with the default property.
After thinking about this a bit, I came up with a few ideas on how to handle this. It is a bit of a hack, but might work: 1) when calling g_type_register(), add a property _pygtk_gobject_wrapper to the new type if it doesn't already exist. The property would be a CONSTRUCT_ONLY property. 2) The set_property handler would check if the property being set was "_pygtk_gobject_wrapper", and if so, associate the wrapper with the new GObject. It would probably print a warning if a wrapper had already been associated with the object. 3) alter __gobject_init__() to implicitly set _pygtk_gobject_wrapper (if the property exists for the GType) as the first property. I think this should cut in before any implicit properties are set (or any other properties for that matter).
James, do you plan on working on this?
I have just discovered something interesting. I was about to make a patch to propose a g_object_set_constructor_property, but then I discovered in the code that construct-only properties can be set with g_object_set_property if the object is in a "in construction" state. GObjects enter such state before being returned by g_type_create_instance, and leave it when the constructor() virtual returns. Therefore, supporting construc-only properties in pygtk is not so complicated: 1. We override GObjectClass->constructor; in it we: a) create an instance (g_type_create_instance) b) create python wrapper and attach it to the GObject instance c) set the construction properties, using g_object_set_property Steps a)+c) are normal GObject constructor procedures; we just need to add b). To add b), we need to be able to pass arbitrary parameters (data/qdata) into the constructor. That last part is more complicated...
Rather than calling g_type_create_instance() directly, you should chain up to the parent's ->constructor() implementation (g_type_create_instance() is apparently only meant to be used by code that is implementing new fundamental types). You can easily filter the construct properties list before passing it to the parent constructor though (assuming there is an easy way to tell which properties have a Python setter implementation behind them).
OK, chaining up to parent constructor could be done I guess. Filtering out _all_ properties before chaining shouldn't be a problem, as we can set them afterwards in the outer constructor. What really worries me about chaining to parent constructor is this (in g_object_constructor): GObjectNotifyQueue *nqueue = g_object_notify_queue_freeze (object, &property_notify_context); /* ... set properties ... */ g_object_notify_queue_thaw (object, nqueue); I'm not so sure of the side-effecs of these "notify queue" freeze/thaw calls. Do you think it's ok to call them one time without changing any properties, and later on calling them again this time with properties changed? If so, then I agree we should chain the constructor, but we need the patch in bug 305560 first. PS1: I have uploaded some additional notes bug #161177. PS2: I am tempted to mark this bus as dup of bug #161177, as I think the problems 1) constructor properties and 2) creating PyGObject from g_object_new, should both be solved in an integrated way, at the same time.
I didn't say it explicitly, but one of the other reasons for calling the parent class's ->constructor is that pygtk is not the only piece of code that overrides the constructor -- from a quick look, GtkButton, GtkCList, GtkCTree, GtkFileChooserButton, GtkFileChooserDefault, GtkFileChooserDialog, GtkFileChooserWidget, GtkInvisible, GtkLayout, GnomeIconList and GnomeAppbar all provide custom constructors, and will presumably malfunction if those constructors aren't called. From what I can tell, g_object_notify_queue_freeze/thaw are just internal versions of g_object_freeze_notify/thaw_notify (the latter appear to be wrappers round the former with some additional safety checks). The effect of the notify freeze/thaw code is to reduce the number of "notify" signals that get emitted. If I call g_object_set_property() twice for the property "foo", then the "notify::foo" signal will be emitted twice. If I wrap the _set_property() calls with a _freeze_notify()/_thaw_notify() pair, then "notify::foo" will only be emitted once (on the _thaw_notify() call). If no properties get changed within the freeze/thaw pair, then there will be no effect.
*** This bug has been marked as a duplicate of 161177 ***