diff options
-rw-r--r-- | ranger/ext/signal_dispatcher.py | 30 | ||||
-rw-r--r-- | test/tc_signal.py | 54 |
2 files changed, 74 insertions, 10 deletions
diff --git a/ranger/ext/signal_dispatcher.py b/ranger/ext/signal_dispatcher.py index 95bc11f3..2762493b 100644 --- a/ranger/ext/signal_dispatcher.py +++ b/ranger/ext/signal_dispatcher.py @@ -14,6 +14,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import re +import weakref class Signal(dict): stopped = False @@ -41,13 +42,15 @@ class SignalDispatcher(object): signal_clear = __init__ - def signal_bind(self, signal_name, function, priority=0.5): + def signal_bind(self, signal_name, function, priority=0.5, weak=False): assert isinstance(signal_name, str) try: handlers = self._signals[signal_name] except: handlers = self._signals[signal_name] = [] nargs = function.__code__.co_argcount - hasattr(function, 'im_func') + if weak: + function = weakref.proxy(function) handler = SignalHandler(signal_name, function, priority, nargs > 0) handlers.append(handler) handlers.sort(key=lambda handler: -handler.priority) @@ -56,10 +59,13 @@ class SignalDispatcher(object): def signal_unbind(self, signal_handler): try: handlers = self._signals[signal_handler.signal_name] - except KeyError: + except: pass else: - handlers.remove(signal_handler) + try: + handlers.remove(signal_handler) + except: + pass def signal_emit(self, signal_name, **kw): assert isinstance(signal_name, str) @@ -72,14 +78,18 @@ class SignalDispatcher(object): signal = Signal(origin=self, name=signal_name, **kw) - for handler in handlers: # propagate + # propagate + for handler in tuple(handlers): if handler.active: - if handler.pass_signal: - handler.function(signal) - else: - handler.function() - if signal.stopped: - return + try: + if handler.pass_signal: + handler.function(signal) + else: + handler.function() + if signal.stopped: + return + except ReferenceError: + handlers.remove(handler) class RegexpSignalDispatcher(SignalDispatcher): """ diff --git a/test/tc_signal.py b/test/tc_signal.py index a585a86b..639cf7c3 100644 --- a/test/tc_signal.py +++ b/test/tc_signal.py @@ -15,6 +15,7 @@ if __name__ == '__main__': from __init__ import init; init() import unittest +import gc from ranger.ext.signal_dispatcher import * class TestSignal(unittest.TestCase): @@ -75,6 +76,59 @@ class TestSignal(unittest.TestCase): sd.signal_emit('setnumber', number=100) self.assertEqual(None, lst[-1]) + def test_weak_refs(self): + sd = self.sd + is_deleted = [False] + + class Foo(object): + def __init__(self): + self.alphabet = ['a'] + def calc(self, signal): + self.alphabet.append(chr(ord(self.alphabet[-1]) + 1)) + def __del__(self): + is_deleted[0] = True + + foo = Foo() + alphabet = foo.alphabet + calc = foo.calc + + del foo + self.assertEqual('a', ''.join(alphabet)) + sd.signal_bind('mysignal', calc, weak=True) + sd.signal_emit('mysignal') + self.assertEqual('ab', ''.join(alphabet)) + self.assertFalse(is_deleted[0]) + + del calc + self.assertTrue(is_deleted[0]) + + def test_weak_refs_dead_on_arrival(self): + sd = self.sd + is_deleted = [False] + + class Foo(object): + def __init__(self): + self.alphabet = ['a'] + def calc(self, signal): + self.alphabet.append(chr(ord(self.alphabet[-1]) + 1)) + def __del__(self): + is_deleted[0] = True + + foo = Foo() + alphabet = foo.alphabet + + self.assertEqual('a', ''.join(alphabet)) + sd.signal_bind('mysignal', foo.calc, weak=True) + del foo + + sd.signal_emit('mysignal') + self.assertEqual('ab', ''.join(alphabet)) + self.assertFalse(is_deleted[0]) + + del calc + self.assertTrue(is_deleted[0]) + + def test_regexp_signals(self): sd = RegexpSignalDispatcher() lst = [] |