summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/ext/signal_dispatcher.py30
-rw-r--r--test/tc_signal.py54
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 = []
>281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325