summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Guchevskij <rvtpro@gmail.com>2023-03-28 20:09:34 +0300
committerGitHub <noreply@github.com>2023-03-28 19:09:34 +0200
commit6d2393bbeed3757eea6cbb4b33a14cb9960e597d (patch)
treecd9811505572b3462158871cf6ba835ac52589bd
parentb593cf7645b6b9b5377945313f04f0d6a293fbfb (diff)
downloadurwid-6d2393bbeed3757eea6cbb4b33a14cb9960e597d.tar.gz
Fix TypeError in signals module on weak object dispose (#503)
squash and merge
-rw-r--r--urwid/signals.py6
-rw-r--r--urwid/tests/test_signals.py62
2 files changed, 63 insertions, 5 deletions
diff --git a/urwid/signals.py b/urwid/signals.py
index 0269dfd..78f1eae 100644
--- a/urwid/signals.py
+++ b/urwid/signals.py
@@ -174,10 +174,7 @@ class Signals(object):
def weakref_callback(weakref):
o = obj_weak()
if o:
- try:
- del getattr(o, self._signal_attr, {})[name][key]
- except KeyError:
- pass
+ self.disconnect_by_key(o, name, key)
user_args = self._prepare_user_args(weak_args, user_args, weakref_callback)
handlers.append((key, callback, user_arg, user_args))
@@ -300,4 +297,3 @@ register_signal = _signals.register
connect_signal = _signals.connect
disconnect_signal = _signals.disconnect
disconnect_signal_by_key = _signals.disconnect_by_key
-
diff --git a/urwid/tests/test_signals.py b/urwid/tests/test_signals.py
new file mode 100644
index 0000000..22db34e
--- /dev/null
+++ b/urwid/tests/test_signals.py
@@ -0,0 +1,62 @@
+import unittest
+try:
+ from unittest.mock import Mock
+except ImportError:
+ # Python2, rely on PyPi
+ from mock import Mock
+
+from urwid import connect_signal, disconnect_signal, register_signal, emit_signal, Signals, Edit
+
+class SiglnalsTest(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.EmClass = type("EmClass", (object, ), {})
+ register_signal(cls.EmClass, ["change", "test"])
+
+ def test_connect(self):
+ obj = Mock()
+ handler = Mock()
+ edit = Edit('')
+ key = connect_signal(edit, 'change', handler, user_args=[obj])
+ self.assertIsNotNone(key)
+ edit.set_edit_text('long test text')
+ handler.assert_called_once_with(obj, edit, 'long test text')
+
+ handler.reset_mock()
+ disconnect_signal(edit, 'change', handler, user_args=[obj])
+ edit.set_edit_text('another text')
+ handler.assert_not_called()
+
+ def test_weak_del(self):
+ emitter = SiglnalsTest.EmClass()
+ w1 = Mock()
+ w2 = Mock()
+ w3 = Mock()
+
+ handler1 = Mock()
+ handler2 = Mock()
+
+ k1 = connect_signal(emitter, 'test', handler1, weak_args=[w1], user_args=[42, "abc"])
+ k2 = connect_signal(emitter, 'test', handler2, weak_args=[w2, w3], user_args=[8])
+ self.assertIsNotNone(k2)
+
+ emit_signal(emitter, 'test', "Foo")
+ handler1.assert_called_once_with(w1, 42, "abc", "Foo")
+ handler2.assert_called_once_with(w2, w3, 8, "Foo")
+
+ handler1.reset_mock()
+ handler2.reset_mock()
+ del w1
+ self.assertEqual(len(getattr(emitter, Signals._signal_attr)['test']), 1)
+ emit_signal(emitter, 'test', "Bar")
+ handler1.assert_not_called()
+ handler2.assert_called_once_with(w2, w3, 8, "Bar")
+
+ handler2.reset_mock()
+ del w3
+ emit_signal(emitter, 'test', "Baz")
+ handler1.assert_not_called()
+ handler2.assert_not_called()
+ self.assertEqual(len(getattr(emitter, Signals._signal_attr)['test']), 0)
+ del w2