A handler connected by the connect_object(..., gobject) is not disconnected when the gobject is destroyed
A signal handler connected by the connect_object(..., gobject)
is not disconnected automatically when the gobject
is destroyed.
The following test code reproduces this problem.
from gi.repository import GObject, Gio
obj = GObject.Object()
action = Gio.SimpleAction.new('test')
action.set_enabled(True)
def on_enabled_changed(obj, pspec):
print('on_enabled_changed, obj:', obj)
action.connect_object('notify::enabled', on_enabled_changed, obj)
del obj
action.set_enabled(False)
The above code is expected to output nothing, because the obj
is destroyed before the enabled property is changed.
But actually it outputs like the following.
on_enabled_changed, obj: <GObject.Object object at 0x7f6ac4be4c40 (GObject at 0x22a8000)>
The following equivalent C code works as expected.
#include <stdio.h>
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
void on_enabled_changed(GSimpleAction* action, GParamSpec *pspec, GObject *obj)
{
printf("on_enabled_changed, obj:%p\n", obj);
}
int main()
{
GObject *obj = g_object_new(G_TYPE_OBJECT, NULL);
GSimpleAction *action = g_simple_action_new("test", NULL);
g_simple_action_set_enabled(action, TRUE);
g_signal_connect_object(action, "notify::enabled", G_CALLBACK(on_enabled_changed), obj, 0);
g_object_unref(obj);
g_simple_action_set_enabled(action, FALSE);
g_object_unref(action);
return 0;
}
I analyzed the source code. The implementation of pygobject_connect_object()
, connect_helper()
and pygi_signal_closure_new()
does not care for this automatic disconnection. It ensures the gobject
is alive while the signal owner is alive, prohibiting the deletion of the gobject
argument(, kept in pc->swap_data
).
The C GLib implements this automatic disconnection by calling g_object_watch_closure()
in g_closure_new_object()
.
I have read old issues - #36 and #1. Their main concern seems to be covering general Python objects, not just for the gobject
argument of connect_object()
, but also for the handler
and data
arguments of connect()
. IMHO, this discrepancy of connect_object()
should be dealt with at first, because the C counterpart g_signal_connect_object()
is usually used when the life-cycle of a signal owner and a signal-handling object mismatch, and this cannot be done in Python.