def testSimpleAmbiguousChooser(self): # {{{ """Default or unknown policy generates error on ambiguity""" var = [] def DummyNew1(var): # {{{ var.append("NEW1") return 1 # End def #}}} def DummyNew2(var): # {{{ var.append("NEW2") return 2 # End def #}}} def choose1(var): return True def choose2(var): return True s = Signal(DummyFunction) c = [(choose1, DummyNew1), (choose2, DummyNew2)] s.connect(choose=c) s.chooserpolicy = None try: ret = s(var) self.assert_(False) except AmbiguousChoiceError, err: self.assertEqual(str(err).strip(), "Found more than one selectable callable")
def testSimpleDefaultChooser(self): # {{{ """If can't decide, choose original callable""" var = [] def DummyNew1(var): # {{{ var.append("NEW1") return 1 # End def #}}} def DummyNew2(var): # {{{ var.append("NEW2") return 2 # End def #}}} def choose1(var): return True def choose2(var): return True s = Signal(DummyFunction) c = [(choose1, DummyNew1), (choose2, DummyNew2)] s.connect(choose=c) s.chooserpolicy = "default" ret = s(var) self.assertEqual(ret, 1) self.assertEqual(var, ["DUMMY"])
def testSimpleLastChooser(self): # {{{ """Choose last acceptable choice""" var = [] def DummyNew1(var): # {{{ var.append("NEW1") return 1 # End def #}}} def DummyNew2(var): # {{{ var.append("NEW2") return 2 # End def #}}} def choose1(var): return True def choose2(var): return True s = Signal(DummyFunction) c = [(choose1, DummyNew1), (choose2, DummyNew2)] s.connect(choose=c) s.chooserpolicy = "last" ret = s(var) self.assertEqual(ret, 2) self.assertEqual(var, ["NEW2"])
def testUnexpectedArgs(self): #{{{ '''Passing in unexpected keyword arguments illegal''' s = Signal(DummyFunction) try: s.connect(hello=1) self.assert_(False) except ValueError, err: self.assertEqual(str(err).strip(), 'valid keyword arguments are: before, around, onreturn, choose, weak, weakcondf')
def testSimpleBefore(self): # {{{ """Callable connected before runs before""" def runbefore(var): # {{{ var.append("BEFORE") # End def #}}} var = [] s = Signal(DummyFunction) s.connect(before=[runbefore]) ret = s(var) self.assertEqual(var, ["BEFORE", "DUMMY"]) self.assertEqual(ret, 1)
def testSimpleAfter(self): # {{{ """Callable connected after runs after""" def runafter(var): # {{{ var.append("AFTER") # End def #}}} var = [] s = Signal(DummyFunction) s.connect(runafter) ret = s(var) self.assertEqual(var, ["DUMMY", "AFTER"]) self.assertEqual(ret, 1)
def testSimpleReturn(self): # {{{ """Callable connected return runs return""" var = [] def onreturn(ret): # {{{ var.append(("RETURN", ret)) # End def #}}} s = Signal(DummyFunction) s.connect(onreturn=[onreturn]) ret = s(var) self.assertEqual(var, ["DUMMY", ("RETURN", 1)]) self.assertEqual(ret, 1)
def testAddSameToDifferentSlots(self): #{{{ '''Adding the same function to different slot allowed''' def a(): #{{{ pass # End def #}}} aid = id(a) s = Signal(DummyFunction) s.connect(a, before=[a]) self.assertEqual(len(s._afterfunc), 1) self.assertEqual(len(s._beforefunc), 1) self.assertEqual(s._afterfunc[0].cid, aid) self.assertEqual(s._beforefunc[0].cid, aid)
def testNonFunction(self): #{{{ '''Passing in a non-function argument is illegal''' check = ('after_slots', 'before', 'around') for arg in check: #{{{ res = False s = Signal(DummyFunction) try: if arg == 'after_slots': s.connect(1) else: s.connect(**{arg: [1]}) except TypeError, err: msg = 'Detected non-callable element of %s sequence' %arg res = str(err).strip() == msg if not res: raise Exception(str(err) + ' ::: ' + msg) self.assert_(res)
def testAddInstanceMethod(self): #{{{ '''Connecting an instance method''' class A(object): #{{{ def test(self): #{{{ return self.__class__.__name__ # End def #}}} # End class #}}} class B(object): #{{{ def test(self): #{{{ return self.__class__.__name__ # End def #}}} # End class #}}} a = A() b = B() s = Signal(a.test) s.connect(b.test) self.assert_(True)
def testSimpleChooser(self): # {{{ """Pushing in an always true chooser runs it instead""" var = [] def DummyNew(var): # {{{ var.append("NEW") return 2 # End def #}}} def choosenew(var): return True s = Signal(DummyFunction) s.connect(choose=[(choosenew, DummyNew)]) ret = s(var) self.assertEqual(ret, 2) self.assertEqual(var, ["NEW"])
def testChangeChooser(self): #{{{ '''After connecting a chooser and its callable, change the chooser''' def DummyNew(a): #{{{ return 'hello %s' %str(a) # End def #}}} def choose1(a): #{{{ return True # End def #}}} def choose2(a): #{{{ return False # End def #}}} s = Signal(DummyFunction) s.connect(choose=[(choose1, DummyNew)]) s.connect(choose=[(choose2, DummyNew)]) self.assertEqual(len(s._choosefunc), 1) self.assertEqual(s._choosefunc[0].choosefunc.cid, cid(choose2)) ret = s('world') self.assertEqual(ret, 'world')
def testFirstAdd(self): #{{{ '''Add brand new function''' check = ('after_slots', 'before') def test_add(a): #{{{ pass # End def #}}} taid = id(test_add) for arg in check: #{{{ res = False s = Signal(DummyFunction) l = None if arg == 'after_slots': s.connect(test_add) l = s._afterfunc else: s.connect(**{arg: [test_add]}) l = eval('s._%sfunc' %arg) self.assertEqual(len(l), 1) self.assertEqual(l[0].cid, taid)
def testSimpleAround(self): # {{{ """Callable connected around runs around""" def wrap_around(func): # {{{ def runaround(s, var): var.append("AROUND FIRST") ret = func(var) var.append("AROUND LAST") return ret return runaround # End def #}}} var = [] s = Signal(DummyFunction) s.connect(around=[wrap_around]) ret = s(var) self.assertEqual(var, ["AROUND FIRST", "DUMMY", "AROUND LAST"]) self.assertEqual(ret, 1)
def testDerefInstanceMethod(self): #{{{ '''Dereferencing a connected instance method cleans the appropriate list''' class A(object): #{{{ def test(self): #{{{ return self.__class__.__name__ # End def #}}} # End class #}}} class B(object): #{{{ def test(self): #{{{ return self.__class__.__name__ # End def #}}} # End class #}}} a = A() b = B() s = Signal(a.test) s.connect(b.test) s.connect(b.test) self.assertEqual(len(s._afterfunc), 1) self.assertEqual(cid(s._afterfunc[0]), cid(b.test)) del b self.assertEqual(len(s._afterfunc), 0)
def testExistingAdd(self): #{{{ '''Add existing function''' check = ('after_slots', 'before') def a(a): #{{{ pass # End def #}}} def b(a): #{{{ pass # End def #}}} bid = id(b) for arg in check: #{{{ res = False s = Signal(DummyFunction) l = None if arg == 'after_slots': s.connect(a, b) s.connect(b) l = s._afterfunc else: s.connect(**{arg: [a, b]}) s.connect(**{arg: [b]}) l = eval('s._%sfunc' %arg) self.assertEqual(len(l), 2) self.assertEqual(l[1].cid, bid)
def testExistingInstanceMethod(self): #{{{ '''Does not add already existing instance method''' class A(object): #{{{ def test(self): #{{{ return self.__class__.__name__ # End def #}}} # End class #}}} class B(object): #{{{ def test(self): #{{{ return self.__class__.__name__ # End def #}}} # End class #}}} a = A() b = B() s = Signal(a.test) s.connect(b.test) s.connect(b.test) s.connect(b.test) s.connect(b.test) s.connect(b.test) self.assertEqual(len(s._afterfunc), 1) self.assertEqual(cid(s._afterfunc[0]), cid(b.test))
def subscribe(iobj, target, ftype=None, choosefunc=None): #{{{ _validate_subscribekw(iobj, target, ftype, choosefunc) i = issue(iobj) if i is None: if ftype: raise TypeError('Please subscribe an issue first.') i = Signal(target) _siglist.append((iobj, i)) elif not ftype or ftype == 'after': i.connect(target) elif ftype == 'before': i.connect(before=[target]) elif ftype == 'around': i.connect(around=[target]) elif ftype == 'onreturn': i.connect(onreturn=[target]) elif ftype == 'choose': i.connect(choose=[(choosefunc, target)]) return i
def subscribe(iobj, target, msgobj=Message, argsobj=Arguments, ftype=None, choosefunc=None): #{{{ _validate_subscribekw(iobj, target, msgobj, argsobj, ftype, choosefunc) i = issue(iobj, msgobj, argsobj) if i is None: if ftype: raise TypeError('Please subscribe an issue first.') i = Signal(target) cobj = callableobj(target) numargs = i.func.numargs if _isf(cobj) and numargs != 1: raise ValueError("'target' argument is a function: it must accept exactly 1 argument; instead got %i" %numargs) elif _ism(cobj) and numargs != 2: raise ValueError("'target' argument is a method: it must accept exactly 2 arguments; instead got %i" %numargs) smsg = _siglist.get(iobj, {}) sargs = smsg.get(msgobj, {}) sargs.update({argsobj: i}) smsg.update({msgobj: sargs}) _siglist.update({iobj: smsg}) elif not ftype or ftype == 'after': i.connect(target) elif ftype == 'before': i.connect(before=[target]) elif ftype == 'around': i.connect(around=[target]) elif ftype == 'onreturn': i.connect(onreturn=[target]) elif ftype == 'choose': i.connect(choose=[(choosefunc, target)]) return i