From 1c1b6c31de513f0f056a915bf7d8e608c485ae50 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 30 Mar 2010 04:50:38 +0200 Subject: added SignalDispatcher class --- test/tc_signal.py | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 test/tc_signal.py (limited to 'test/tc_signal.py') diff --git a/test/tc_signal.py b/test/tc_signal.py new file mode 100644 index 00000000..67625505 --- /dev/null +++ b/test/tc_signal.py @@ -0,0 +1,79 @@ +# Copyright (C) 2009, 2010 Roman Zimbelmann +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if __name__ == '__main__': from __init__ import init; init() +import unittest +from ranger.ext.signal_dispatcher import SignalDispatcher + +class TestSignal(unittest.TestCase): + def setUp(self): + self.sd = SignalDispatcher() + + def test_signal_register_emit(self): + sd = self.sd + def poo(sig): + self.assert_('works' in sig) + self.assertEqual('yes', sig.works) + handler = sd.signal_bind('x', poo) + + sd.signal_emit('x', works='yes') + sd.signal_unbind(handler) + sd.signal_emit('x') + + def test_signal_order(self): + sd = self.sd + lst = [] + def addn(n): + return lambda _: lst.append(n) + + sd.signal_bind('x', addn(6)) + sd.signal_bind('x', addn(3), priority=1) + sd.signal_bind('x', addn(2), priority=1) + sd.signal_bind('x', addn(9), priority=0) + sd.signal_bind('x', addn(1337), priority=0.7) + sd.signal_emit('x') + + self.assert_(lst.index(3) < lst.index(6)) + self.assert_(lst.index(2) < lst.index(6)) + self.assert_(lst.index(6) < lst.index(9)) + self.assert_(lst.index(1337) < lst.index(6)) + self.assert_(lst.index(1337) < lst.index(9)) + self.assert_(lst.index(1337) > lst.index(2)) + + def test_modifying_arguments(self): + sd = self.sd + lst = [] + def modify(s): + s.number = 5 + def set_number(s): + lst.append(s.number) + def stopit(s): + s.stop() + + sd.signal_bind('setnumber', set_number) + sd.signal_emit('setnumber', number=100) + self.assertEqual(100, lst[-1]) + + sd.signal_bind('setnumber', modify, priority=1) + sd.signal_emit('setnumber', number=100) + self.assertEqual(5, lst[-1]) + + lst.append(None) + sd.signal_bind('setnumber', stopit, priority=1) + sd.signal_emit('setnumber', number=100) + self.assertEqual(None, lst[-1]) + +if __name__ == '__main__': + unittest.main() -- cgit 1.4.1-2-gfad0 From b4f58d4600fde7c1aceee1e420ccc0c32b46e2bf Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 30 Mar 2010 15:58:28 +0200 Subject: added RegexpSignalDispatcher though im not sure if ill use it --- ranger/ext/signal_dispatcher.py | 53 +++++++++++++++++++++++++++++++++++++++++ test/tc_signal.py | 44 +++++++++++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 1 deletion(-) (limited to 'test/tc_signal.py') diff --git a/ranger/ext/signal_dispatcher.py b/ranger/ext/signal_dispatcher.py index 542fb472..95bc11f3 100644 --- a/ranger/ext/signal_dispatcher.py +++ b/ranger/ext/signal_dispatcher.py @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import re + class Signal(dict): stopped = False def __init__(self, **keywords): @@ -25,6 +27,7 @@ class Signal(dict): class SignalHandler(object): active = True + regexp = None def __init__(self, signal_name, function, priority, pass_signal): self.priority = max(0, min(1, priority)) self.signal_name = signal_name @@ -77,3 +80,53 @@ class SignalDispatcher(object): handler.function() if signal.stopped: return + +class RegexpSignalDispatcher(SignalDispatcher): + """ + A subclass of SignalDispatcher with regexp matching. + """ + + def __init__(self): + SignalDispatcher.__init__(self) + self._signal_regexes = list() + _signal_clear = __init__ + + def signal_bind(self, signal_name, function, priority=0.5): + try: + handlers = self._signals[signal_name] + except: + handlers = self._signals[signal_name] = [] + for handler in self._signal_regexes: + if handler.regexp.match(signal_name): + handlers.append(handler) + return SignalDispatcher.signal_bind(self, signal_name, \ + function, priority) + + def signal_bind_regexp(self, regexp, function, priority=0.5): + if isinstance(regexp, str): + regexp = re.compile(regexp) + handler = self.signal_bind('dynamic', function, priority) + handler.regexp = regexp + self._signal_regexes.append(handler) + for signal_name, handlers in self._signals.items(): + if regexp.match(signal_name): + handlers.append(handler) + handlers.sort(key=lambda handler: -handler.priority) + return handler + + def signal_unbind(self, handler): + self._signal_regexes.remove(handler) + for handlers in self._signals.values(): + try: + handlers.remove(handler) + except ValueError: + pass + + def signal_emit(self, signal_name, **kw): + assert isinstance(signal_name, str) + if not signal_name in self._signals: + handlers = self._signals[signal_name] = [] + for handler in self._signal_regexes: + if handler.regexp.match(signal_name): + handlers.append(handler) + SignalDispatcher.signal_emit(self, signal_name, **kw) diff --git a/test/tc_signal.py b/test/tc_signal.py index 67625505..a585a86b 100644 --- a/test/tc_signal.py +++ b/test/tc_signal.py @@ -15,7 +15,7 @@ if __name__ == '__main__': from __init__ import init; init() import unittest -from ranger.ext.signal_dispatcher import SignalDispatcher +from ranger.ext.signal_dispatcher import * class TestSignal(unittest.TestCase): def setUp(self): @@ -75,5 +75,47 @@ class TestSignal(unittest.TestCase): sd.signal_emit('setnumber', number=100) self.assertEqual(None, lst[-1]) + def test_regexp_signals(self): + sd = RegexpSignalDispatcher() + lst = [] + def modify(s): + s.number = 5 + def set_number(s): + lst.append(s.number) + def stopit(s): + s.stop() + + h1 = sd.signal_bind_regexp('.*ar', modify, priority=1) + + sd.signal_bind('xyz', set_number) + sd.signal_bind('foobar', set_number) + + sd.signal_emit('xyz', number=10) + self.assertEqual(10, lst[-1]) + sd.signal_emit('foobar', number=10) + self.assertEqual(5, lst[-1]) + + h2 = sd.signal_bind_regexp('x.z', modify, priority=1) +# print(sd._signals) + + sd.signal_emit('xyz', number=10) + self.assertEqual(5, lst[-1]) + sd.signal_emit('foobar', number=10) + self.assertEqual(5, lst[-1]) + + sd.signal_unbind(h2) + + sd.signal_emit('xyz', number=10) + self.assertEqual(10, lst[-1]) + sd.signal_emit('foobar', number=10) + self.assertEqual(5, lst[-1]) + + sd.signal_unbind(h1) + + sd.signal_emit('xyz', number=10) + self.assertEqual(10, lst[-1]) + sd.signal_emit('foobar', number=10) + self.assertEqual(10, lst[-1]) + if __name__ == '__main__': unittest.main() -- cgit 1.4.1-2-gfad0 From d1fc886688016e97af8f1dbe74e5b2c04649e08b Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 30 Mar 2010 19:13:58 +0200 Subject: tc_signal: added more tests (some still fail!) --- ranger/ext/signal_dispatcher.py | 30 +++++++++++++++-------- test/tc_signal.py | 54 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 10 deletions(-) (limited to 'test/tc_signal.py') 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 . 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 = [] -- cgit 1.4.1-2-gfad0 From da0771094b1a1e10c70e9e359296ae105b7f6ea2 Mon Sep 17 00:00:00 2001 From: hut Date: Tue, 30 Mar 2010 19:55:17 +0200 Subject: ext.signal_dispatcher: fixed weak refs to bound methods --- ranger/ext/signal_dispatcher.py | 23 ++++++++++++++++++----- test/tc_signal.py | 7 +++++-- 2 files changed, 23 insertions(+), 7 deletions(-) (limited to 'test/tc_signal.py') diff --git a/ranger/ext/signal_dispatcher.py b/ranger/ext/signal_dispatcher.py index 2762493b..11e9c955 100644 --- a/ranger/ext/signal_dispatcher.py +++ b/ranger/ext/signal_dispatcher.py @@ -15,6 +15,7 @@ import re import weakref +from types import MethodType class Signal(dict): stopped = False @@ -48,9 +49,17 @@ class SignalDispatcher(object): 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) + nargs = function.__code__.co_argcount + + try: + instance = function.__self__ + except: + if weak: + function = weakref.proxy(function) + else: + nargs -= 1 + if weak: + function = (function.__func__, weakref.proxy(function.__self__)) handler = SignalHandler(signal_name, function, priority, nargs > 0) handlers.append(handler) handlers.sort(key=lambda handler: -handler.priority) @@ -82,10 +91,14 @@ class SignalDispatcher(object): for handler in tuple(handlers): if handler.active: try: + if isinstance(handler.function, tuple): + fnc = MethodType(*handler.function) + else: + fnc = handler.function if handler.pass_signal: - handler.function(signal) + fnc(signal) else: - handler.function() + fnc() if signal.stopped: return except ReferenceError: diff --git a/test/tc_signal.py b/test/tc_signal.py index 639cf7c3..ad04beab 100644 --- a/test/tc_signal.py +++ b/test/tc_signal.py @@ -119,13 +119,16 @@ class TestSignal(unittest.TestCase): self.assertEqual('a', ''.join(alphabet)) sd.signal_bind('mysignal', foo.calc, weak=True) - del foo + print("jambo") sd.signal_emit('mysignal') self.assertEqual('ab', ''.join(alphabet)) self.assertFalse(is_deleted[0]) - del calc + del foo + + sd.signal_emit('mysignal') + self.assertEqual('ab', ''.join(alphabet)) self.assertTrue(is_deleted[0]) -- cgit 1.4.1-2-gfad0 From 96479abb595b8c6c30b1473ada2b78777c7e5f4c Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 31 Mar 2010 05:00:38 +0200 Subject: clean up --- ranger/fsobject/fsobject.py | 1 - test/tc_signal.py | 1 - 2 files changed, 2 deletions(-) (limited to 'test/tc_signal.py') diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py index 6933b18d..1ab3addd 100644 --- a/ranger/fsobject/fsobject.py +++ b/ranger/fsobject/fsobject.py @@ -18,7 +18,6 @@ DOCUMENT_EXTENSIONS = 'pdf doc ppt odt'.split() DOCUMENT_BASENAMES = 'README TODO LICENSE COPYING INSTALL'.split() import time -from ranger import log from . import T_FILE, T_DIRECTORY, T_UNKNOWN, T_NONEXISTANT, BAD_INFO from ranger.shared import MimeTypeAware, FileManagerAware from ranger.ext.shell_escape import shell_escape diff --git a/test/tc_signal.py b/test/tc_signal.py index ad04beab..feba3e5b 100644 --- a/test/tc_signal.py +++ b/test/tc_signal.py @@ -119,7 +119,6 @@ class TestSignal(unittest.TestCase): self.assertEqual('a', ''.join(alphabet)) sd.signal_bind('mysignal', foo.calc, weak=True) - print("jambo") sd.signal_emit('mysignal') self.assertEqual('ab', ''.join(alphabet)) -- cgit 1.4.1-2-gfad0 From c8085cbd1b83ba69ddb76c2d1a4dbfb5b1eb10ea Mon Sep 17 00:00:00 2001 From: hut Date: Wed, 31 Mar 2010 05:16:47 +0200 Subject: removed RegexpSignalDispatcher, I don't use it --- ranger/ext/signal_dispatcher.py | 52 ----------------------------------------- test/tc_signal.py | 43 ---------------------------------- 2 files changed, 95 deletions(-) (limited to 'test/tc_signal.py') diff --git a/ranger/ext/signal_dispatcher.py b/ranger/ext/signal_dispatcher.py index 11e9c955..c1630c0c 100644 --- a/ranger/ext/signal_dispatcher.py +++ b/ranger/ext/signal_dispatcher.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import re import weakref from types import MethodType @@ -29,7 +28,6 @@ class Signal(dict): class SignalHandler(object): active = True - regexp = None def __init__(self, signal_name, function, priority, pass_signal): self.priority = max(0, min(1, priority)) self.signal_name = signal_name @@ -103,53 +101,3 @@ class SignalDispatcher(object): return except ReferenceError: handlers.remove(handler) - -class RegexpSignalDispatcher(SignalDispatcher): - """ - A subclass of SignalDispatcher with regexp matching. - """ - - def __init__(self): - SignalDispatcher.__init__(self) - self._signal_regexes = list() - _signal_clear = __init__ - - def signal_bind(self, signal_name, function, priority=0.5): - try: - handlers = self._signals[signal_name] - except: - handlers = self._signals[signal_name] = [] - for handler in self._signal_regexes: - if handler.regexp.match(signal_name): - handlers.append(handler) - return SignalDispatcher.signal_bind(self, signal_name, \ - function, priority) - - def signal_bind_regexp(self, regexp, function, priority=0.5): - if isinstance(regexp, str): - regexp = re.compile(regexp) - handler = self.signal_bind('dynamic', function, priority) - handler.regexp = regexp - self._signal_regexes.append(handler) - for signal_name, handlers in self._signals.items(): - if regexp.match(signal_name): - handlers.append(handler) - handlers.sort(key=lambda handler: -handler.priority) - return handler - - def signal_unbind(self, handler): - self._signal_regexes.remove(handler) - for handlers in self._signals.values(): - try: - handlers.remove(handler) - except ValueError: - pass - - def signal_emit(self, signal_name, **kw): - assert isinstance(signal_name, str) - if not signal_name in self._signals: - handlers = self._signals[signal_name] = [] - for handler in self._signal_regexes: - if handler.regexp.match(signal_name): - handlers.append(handler) - SignalDispatcher.signal_emit(self, signal_name, **kw) diff --git a/test/tc_signal.py b/test/tc_signal.py index feba3e5b..35b4eebe 100644 --- a/test/tc_signal.py +++ b/test/tc_signal.py @@ -130,48 +130,5 @@ class TestSignal(unittest.TestCase): self.assertEqual('ab', ''.join(alphabet)) self.assertTrue(is_deleted[0]) - - def test_regexp_signals(self): - sd = RegexpSignalDispatcher() - lst = [] - def modify(s): - s.number = 5 - def set_number(s): - lst.append(s.number) - def stopit(s): - s.stop() - - h1 = sd.signal_bind_regexp('.*ar', modify, priority=1) - - sd.signal_bind('xyz', set_number) - sd.signal_bind('foobar', set_number) - - sd.signal_emit('xyz', number=10) - self.assertEqual(10, lst[-1]) - sd.signal_emit('foobar', number=10) - self.assertEqual(5, lst[-1]) - - h2 = sd.signal_bind_regexp('x.z', modify, priority=1) -# print(sd._signals) - - sd.signal_emit('xyz', number=10) - self.assertEqual(5, lst[-1]) - sd.signal_emit('foobar', number=10) - self.assertEqual(5, lst[-1]) - - sd.signal_unbind(h2) - - sd.signal_emit('xyz', number=10) - self.assertEqual(10, lst[-1]) - sd.signal_emit('foobar', number=10) - self.assertEqual(5, lst[-1]) - - sd.signal_unbind(h1) - - sd.signal_emit('xyz', number=10) - self.assertEqual(10, lst[-1]) - sd.signal_emit('foobar', number=10) - self.assertEqual(10, lst[-1]) - if __name__ == '__main__': unittest.main() -- cgit 1.4.1-2-gfad0