diff options
author | Matthijs Kooijman <matthijs@stdin.nl> | 2012-03-27 13:46:24 +0200 |
---|---|---|
committer | Matthijs Kooijman <matthijs@stdin.nl> | 2012-12-14 17:50:26 +0100 |
commit | c59c148d68d64b4b1cd38fb4813f4910714a8b62 (patch) | |
tree | 42c147029c1d72ef4fa69425a97343cc4d9b6b28 /urwid | |
parent | b55f7d1249d83a57aaa099394ba6d40339a9a3bb (diff) | |
download | urwid-c59c148d68d64b4b1cd38fb4813f4910714a8b62.tar.gz |
use weakref callbacks to automatically disconnect signals
When any of the weakref arguments to a signal handler are garbage
collected, the signal handler is disconnect (since calling it no longer
make sense without all the arguments).
Diffstat (limited to 'urwid')
-rw-r--r-- | urwid/signals.py | 37 |
1 files changed, 29 insertions, 8 deletions
diff --git a/urwid/signals.py b/urwid/signals.py index 2f7f701..7ac6f5c 100644 --- a/urwid/signals.py +++ b/urwid/signals.py @@ -156,17 +156,37 @@ class Signals(object): # Just generate an arbitrary (but unique) key key = Key() - user_args = self._prepare_user_args(weak_args, user_args) signals = setdefaultattr(obj, self._signal_attr, {}) handlers = signals.setdefault(name, {}) + + # Remove the signal handler when any of the weakref'd arguments + # are garbage collected. Note that this means that the handlers + # dictionary can be modified _at any time_, so it should never + # be iterated directly (e.g. iterate only over .keys() and + # .items(), never over .iterkeys(), .iteritems() or the object + # itself). + # We let the callback keep a weakref to the object as well, to + # prevent a circular reference between the handler and the + # object (via the weakrefs, which keep strong references to + # their callbacks) from existing. + obj_weak = weakref.ref(obj) + def weakref_callback(weakref): + o = obj_weak() + if o: + try: + del getattr(o, self._signal_attr, {})[name][key] + except KeyError: + pass + + user_args = self._prepare_user_args(weak_args, user_args, weakref_callback) handlers[key] = (callback, user_arg, user_args) return key - def _prepare_user_args(self, weak_args, user_args): + def _prepare_user_args(self, weak_args, user_args, callback = None): # Turn weak_args into weakrefs and prepend them to user_args - return [weakref.ref(a) for a in weak_args] + user_args + return [weakref.ref(a, callback) for a in weak_args] + user_args def disconnect(self, obj, name, callback, user_arg=None, weak_args=[], user_args=[]): @@ -254,11 +274,12 @@ class Signals(object): if isinstance(arg, weakref.ReferenceType): arg = arg() if arg is None: - # If the weakref is None, the referenced - # object was cleaned up. We just skip the - # entire callback in this case. - # TODO: remove the callback from the list - # when this happens + # If the weakref is None, the referenced object + # was cleaned up. We just skip the entire + # callback in this case. The weakref cleanup + # handler will have removed the callback when + # this happens, so no need to actually remove + # the callback here. return False args_to_pass.append(arg) |