def testCallback(self) -> None: self.args = [None, None] def f1(*args): self.args[0] = args def f2(*args): self.args[1] = args c = Callback() c.Register(f1) c(1, 2) assert self.args[0] == (1, 2) c.Unregister(f1) self.args[0] = None c(10, 20) assert self.args[0] == None def foo(): pass c.Unregister(foo)
def test_extra_args(self) -> None: """ Tests the extra-args parameter in Register method. """ self.zulu_calls: List[Any] = [] def zulu_one(*args): self.zulu_calls.append(args) def zulu_too(*args): self.zulu_calls.append(args) alpha = Callback() alpha.Register(zulu_one, (1, 2)) assert self.zulu_calls == [] alpha("a") assert self.zulu_calls == [(1, 2, "a")] alpha("a", "b", "c") assert self.zulu_calls == [(1, 2, "a"), (1, 2, "a", "b", "c")] # Test a second method with extra-args alpha.Register(zulu_too, (9, )) alpha("a") assert self.zulu_calls == [ (1, 2, "a"), (1, 2, "a", "b", "c"), (1, 2, "a"), (9, "a"), ]
def testCallbacksRegister(self) -> None: """ Callbacks().Register() when used as a context manager unregisters the callbacks automatically when the context ends. """ c1 = Callback() c2 = Callback() events = [] def bar(when): events.append(when) with Callbacks() as callbacks: callbacks.Register(c1, bar) callbacks.Register(c2, bar) c1(when="c1-first") c2(when="c2-first") assert events == ["c1-first", "c2-first"] c1(when="c1-second") c2(when="c2-second") assert events == ["c1-first", "c2-first"]
def testNeedsUnregister(self) -> None: c = Callback() # Even when the function isn't registered, we not raise an error. def Func(): pass # self.assertNotRaises(RuntimeError, c.Unregister, Func) c.Unregister(Func)
def testRemoveCallbackPlain(self) -> None: class C: def __init__(self, name): self.name = name def OnCallback(self): pass def __eq__(self, other): return self.name == other.name def __ne__(self, other): return not self == other instance1 = C("instance") instance2 = C("instance") assert instance1 == instance2 c = Callback() c.Register(instance1.OnCallback) c.Register(instance2.OnCallback) # removing first callback, and checking that it was actually removed as expected c.Unregister(instance1.OnCallback) assert not c.Contains(instance1.OnCallback) == True # self.assertNotRaises(RuntimeError, c.Unregister, instance1.OnCallback) c.Unregister(instance1.OnCallback) # removing second callback, and checking that it was actually removed as expected c.Unregister(instance2.OnCallback) assert not c.Contains(instance2.OnCallback) == True # self.assertNotRaises(RuntimeError, c.Unregister, instance2.OnCallback) c.Unregister(instance2.OnCallback)
def testWeakMethodProxy2(self) -> None: def Foo(): self.called = True proxy = WeakMethodProxy(Foo) c = Callback() c.Register(proxy) c() assert self.called assert c.Contains(proxy)
def testRegisterTwice(self) -> None: self.called = 0 def After(*args): self.called += 1 c = Callback() c.Register(After) c.Register(After) c.Register(After) c() assert self.called == 1
def testCallbackAndPartial(self) -> None: from functools import partial called = [] def Method(a): called.append(a) c = Callback() c.Register(lambda: Method("lambda")) c.Register(partial(Method, "partial")) c() assert called == ["lambda", "partial"]
def testUnregisterAll(self) -> None: c = Callback() # self.assertNotRaises(AttributeError, c.UnregisterAll) c.UnregisterAll() self.called = False def Func(): self.called = True c.Register(Func) c.UnregisterAll() c() assert self.called == False
def testCallbackInstanceWeakRef(self) -> None: class Obj: def __init__(self): self.called = False def __call__(self): self.called = True c = Callback() obj = Obj() c.Register(obj) c() assert c.Contains(obj) assert obj.called obj_ref = weakref.ref(obj) del obj assert obj_ref() is None
def testAfterRemoveCallback(self) -> None: my_object = _MyClass() my_object.SetAlpha(0) my_object.SetBravo(0) # Test After/Remove with a callback event = Callback() After(my_object.SetAlpha, event) event.Register(my_object.SetBravo) my_object.SetAlpha(3) assert my_object.bravo == 3 Remove(my_object.SetAlpha, event) my_object.SetAlpha(4) assert my_object.bravo == 3
def testWeakMethodProxy(self) -> None: class Obj: def Foo(self): self.called = True obj = Obj() proxy = WeakMethodProxy(obj.Foo) c = Callback() c.Register(proxy) c() assert obj.called assert c.Contains(proxy) obj_ref = weakref.ref(obj) del obj assert obj_ref() is None c() assert len(c) == 0
def testKeepStrongReference(self) -> None: class Obj: __CALLBACK_KEEP_STRONG_REFERENCE__ = True def __init__(self): self.called = False def __call__(self): self.called = True c = Callback() obj = Obj() c.Register(obj) c() assert c.Contains(obj) assert obj.called obj_ref = weakref.ref(obj) del obj # Not collected because of __CALLBACK_KEEP_STRONG_REFERENCE__ in the class. assert obj_ref() is not None
def testRemoveCallbackContext(self) -> None: """Callback.Register() returns a context that can be used to unregister that call.""" events = [] def bar(when): events.append(when) contexts = [] c1 = Callback() contexts.append(c1.Register(bar)) c2 = Callback() contexts.append(c2.Register(bar)) c1("c1-first") c2("c2-first") assert events == ["c1-first", "c2-first"] for context in contexts: context.Unregister() c1("c1-second") c2("c2-second") assert events == ["c1-first", "c2-first"]
def testContains(self) -> None: def foo(x): pass c = Callback() assert not c.Contains(foo) c.Register(foo) assert c.Contains(foo) c.Unregister(foo) assert not c.Contains(foo)
class A(object): c = Callback() def __init__(self, **ka): super().__init__(**ka) self.value = 0.0 self.other_value = 0.0 self.c.Register(self._UpdateBValue) def SetValue(self, value): self.value = value self.c(value) def _UpdateBValue(self, new_value): self.other_value = new_value / 0 # division by zero
class B(object): c = Callback() def __init__(self, **ka): super().__init__(**ka) self._a = A() self.value = 0.0 self.c.Register(self._UpdateAValue) def SetValue(self, value): self.value = value self.c(value * 0.1) def _UpdateAValue(self, new_value): self._a.SetValue(new_value)
def testCallbackReceiverDies(self) -> None: class A: def on_foo(dummy, *args): # @NoSelf self.args = args self.args = None a = A() weak_a = weakref.ref(a) foo = Callback() foo.Register(a.on_foo) foo(1, 2) assert self.args == (1, 2) assert weak_a() is a foo(3, 4) assert self.args == (3, 4) assert weak_a() is a del a assert weak_a() is None foo(5, 6) assert self.args == (3, 4)
def testCallbackWithMagicMock(self, mocker) -> None: """ Check that we can register mock.MagicMock objects in callbacks. This makes it easier to test that public callbacks are being called with correct arguments. Usage (in testing, of course): save_listener = mock.MagicMock(spec=lambda: None) project_manager.on_save.Register(save_listener) project_manager.SlotSave() assert save_listener.call_args == mock.call('foo.file', '.txt') Instead of the more traditional: def OnSave(filename, ext): self.filename = filename self.ext = ext self.filename = None self.ext = ext project_manager.on_save.Register(OnSave) project_manager.SlotSave() assert (self.filename, self.ext) == ('foo.file', '.txt') """ c = Callback() with pytest.raises(RuntimeError): c.Register(mocker.MagicMock()) magic_mock = mocker.stub() c = Callback() c.Register(magic_mock) c(10, name="X") assert magic_mock.call_args_list == [mocker.call(10, name="X")] c(20, name="Y") assert magic_mock.call_args_list == [ mocker.call(10, name="X"), mocker.call(20, name="Y"), ] c.Unregister(magic_mock) c(30, name="Z") assert len(magic_mock.call_args_list) == 2
def testHandleErrorOnCallback(self, mocker) -> None: self.called = 0 def After(*args, **kwargs): self.called += 1 raise RuntimeError("test") def After2(*args, **kwargs): self.called += 1 raise RuntimeError("test2") c = Callback() c.Register(After) c.Register(After2) # test the default behaviour: errors are not handled and stop execution as usual self.called = 0 c = Callback() c.Register(After) c.Register(After2) with pytest.raises(RuntimeError): c() assert self.called == 1
def testWeakRefToCallback(self) -> None: c = Callback() c_ref = weakref.ref(c) assert c_ref() is c
def testKeyReusedAfterDead(self, monkeypatch) -> None: self._gotten_key = False def GetKey(*args, **kwargs): self._gotten_key = True return 1 monkeypatch.setattr(Callback, "_GetKey", GetKey) def AfterMethod(*args): pass def AfterMethodB(*args): pass c = Callback() c.Register(AfterMethod) self._gotten_key = False assert not c.Contains(AfterMethodB) assert c.Contains(AfterMethod) assert self._gotten_key # As we made _GetKey return always the same, this will make it remove one and add the # other one, so, the contains will have to check if they're actually the same or not. c.Register(AfterMethodB) self._gotten_key = False assert c.Contains(AfterMethodB) assert not c.Contains(AfterMethod) assert self._gotten_key class A: def __init__(self): self._a = 0 def GetA(self): return self._a def SetA(self, value): self._a = value a = property(GetA, SetA) a = A() # If registering a bound, it doesn't contain the unbound c.Register(a.SetA) assert not c.Contains(AfterMethodB) assert not c.Contains(A.SetA) assert c.Contains(a.SetA) # But if registering an unbound, it contains the bound c.Register(A.SetA) assert not c.Contains(AfterMethodB) assert c.Contains(A.SetA) assert c.Contains(a.SetA) c.Register(a.SetA) assert len(c) == 1 del a assert not c.Contains(AfterMethodB) assert len(c) == 0 a = A() from oop_ext.foundation.callback._callback import _CallbackWrapper c.Register(_CallbackWrapper(WeakMethodRef(a.SetA))) assert len(c) == 1 del a assert not c.Contains(AfterMethodB) assert len(c) == 0