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 154845 - Provide a way to lookup GClosure from a callable PyObject
Provide a way to lookup GClosure from a callable PyObject
Status: RESOLVED FIXED
Product: pygobject
Classification: Bindings
Component: gobject
2.9.0
Other All
: Normal enhancement
: ---
Assigned To: Nobody's working on this now (help wanted and appreciated)
Python bindings maintainers
Depends on:
Blocks:
 
 
Reported: 2004-10-07 23:16 UTC by John Ehresman
Modified: 2006-04-29 18:50 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description John Ehresman 2004-10-07 23:16:37 UTC
Wrap this as a method on GObject so that callables may be disconnected without
keeping the connection id around.  It would be nice to specify some of the match
options with keyword args so something like the following is possible.

def handler(*args):
  return None
obj.connect('notify', handler)
obj.disconnect_matched(func = handler)

Implementing this is nontrivial because it requires some way to get from the
Python callable to the closure.  Callables should be compared using Python
equality semantics so that two different instance method objects for the same
Python instance and method are considered to be equal.
Comment 1 James Henstridge 2004-10-08 02:23:35 UTC
There doesn't seem to be a way to do this, since you can't just get a list of
all the connected closures.

If we stuffed function name into the data member of GClosure, you might be able
to do a data lookup to find the matching handlers.  However, even this will have
some problems if people want to connect methods as signal handlers (which isn't
uncommon).  Testing for pointer equality isn't enough to determine if two method
objects are the same:

>>> class C(object):
...   def method(self): pass
...
>>> c = C()
>>> f1 = c.method
>>> f2 = c.method
>>> f1 == f2
True
>>> id(f1) == id(f2)
False

This is because Python creates a new method object each time you ask for the
function "method" in the context of the instance "c".
Comment 2 Johan (not receiving bugmail) Dahlin 2004-10-08 15:21:52 UTC
However, you can still attach things to a function objects dictionary, that's a
place where the GClosure can be stored.
Tried to do that as quick hack yesterday (using PyCObject_FromVoidPtr) and it
actually seemed to work. The downside is that you can't filter on arguments
though. I'm not sure what should be done if multiple signals are connected to
the same func, perhaps storing a it in a hashtable (not that hash() for a method
always returns the same value)
Comment 3 James Henstridge 2004-10-08 15:43:53 UTC
Given that hash values are not guaranteed to be unique, I'd feel a bit
uncomfortable doing "delete all connections where the handler hashes to hash(X)"
when the user asks to "delete all connections where the handler is X".

Perhaps this is a case where a new libgobject API is needed.  I'm sure Python
isn't the only language that runs into this problem.
Comment 4 Johan (not receiving bugmail) Dahlin 2004-10-08 15:52:56 UTC
So, what about a list on each method type (since it's __dict__ seems to be
persistent), with all the closures that's attached to the current object?

When doing connect, it'd do something like this:

  if not func.__dict.__.has_key('closures'):
     closures = func.__dict__['closures'] = []
  else:
     closures = []
  closure.append(pygclosure)

and disconnect, disconnect_matched would just remove it from the list.

  
Comment 5 John Ehresman 2004-10-08 19:28:07 UTC
Isn't a list of the python objects connected to a signal kept so it can be
traversed when the gc runs?  If so, can't disconnect_matched simply look in the
list?  I don't think disconnect_matched needs to work in O(1) time (in fact it
doesn't at the C level)
Comment 6 James Henstridge 2004-10-12 05:36:02 UTC
I had forgotten about the closure list.  That should make it possible to
implement any style of disconnect routine.

It'd probably end up being O(n^2) time in the worst case (scan object->closures,
call disconnect_matched() for matching closures), but that shouldn't be a big
deal since the number of handlers connected isn't usually large, and it isn't
used that often.
Comment 7 Johan (not receiving bugmail) Dahlin 2006-04-05 15:35:38 UTC
Confirming and updating summary.

The following functions will be easy to wrap once this is fixed:

g_signal_handlers_block_matched
g_signal_handlers_unblock_matched
g_signal_handlers_disconnect_matched

However, they are quite lowlevel, so what we should have is:

GObject.block_by_func()
GObject.unblock_by_func()
GObject.disconnect_by_func()

It's also possible to search for closures and data, but it doesn't 
seem very relevant from Python.

g_signal_handler_find could also be wrapped, but it's probably not too useful.
Comment 8 Colin Watson 2006-04-11 15:29:54 UTC
This would be particularly useful when working with libglade, since glade_xml_signal_autoconnect() et al throw away the connection ids, so any signals you want to be able to block need to be connected by hand.
Comment 9 Johan (not receiving bugmail) Dahlin 2006-04-29 18:50:43 UTC
It proved to be pretty easy to fix.

I added three new methods to gobject.GObject:

  GObject.disconnect_by_func()
  GObject.handler_block_by_func()
  GObject.handler_unblock_by_func()

Checking in gobject/pygobject.c;
/cvs/gnome/gnome-python/pygobject/gobject/pygobject.c,v  <--  pygobject.c
new revision: 1.69; previous revision: 1.68
done
Checking in gobject/pygtype.c;
/cvs/gnome/gnome-python/pygobject/gobject/pygtype.c,v  <--  pygtype.c
new revision: 1.49; previous revision: 1.48
done
Checking in tests/test_signal.py;
/cvs/gnome/gnome-python/pygobject/tests/test_signal.py,v  <--  test_signal.py
new revision: 1.10; previous revision: 1.9
done