def testNormalFunctions(self): #{{{ '''Normal function or unbound method''' _ = lambda: None ub = method(_, None, None) for f in (_, ub): cw = CallableWrapper(f) self.assertFalse(cw._get_ismethod())
def testNonExistantInstance(self): #{{{ '''No longer existing instance returns None ''' A = DummyClass a = A() w = CallableWrapper(a.DummyMethod) del a self.assert_(w._getcallable() is None)
def testDeadWrapper(self): # {{{ """Calling a dead wrapper generates a warning""" A = DummyClass a = A() w = CallableWrapper(a.DummyMethod) del a self.assert_(w._isdead())
def testRemoveInstance(self): #{{{ '''Deleting an instance makes for a dead CallableWrapper''' A = DummyClass a = A() w = CallableWrapper(a.DummyMethod) del a self.assert_(w._isdead())
def testClassMethodCallable(self): #{{{ '''Initializing with a class method callable sets weakref on class and stores method's internal function''' A = DummyClass w = CallableWrapper(A.DummyMethod) self.assert_(w._object is not None) self.assert_(isinstance(w._object, cref)) self.assert_(w._object() is A) self.assert_(w._function is A.DummyMethod.im_func)
def testNormalFunction(self): #{{{ '''Return back normal functions and unbound methods''' _ = (lambda: None) ub_ = method(_, None, None) for f in (_, ub_): cw = CallableWrapper(f) ret = cw._getref()() self.assertEqual(ret, f)
def testRemoveNonMethodCallable(self): #{{{ '''Deleting a non-method callable makes for a dead CallableWrapper''' def DummyFunction(): #{{{ return 'DummyFunction' # End def #}}} w = CallableWrapper(DummyFunction) del DummyFunction self.assert_(w._isdead())
def testDeadReferences(self): #{{{ '''If the reference dies, return None''' class _(object): #{{{ def me(self): #{{{ pass # End def #}}} # End class #}}} t = _() cw = CallableWrapper(t.me, weak=True) del t self.assertTrue(cw._getref()() is None)
def testBoundInstanceMethods(self): #{{{ '''Bound or instance methods''' class _(object): #{{{ def me(self): #{{{ pass # End def #}}} # End class #}}} t = _() for f in (_.me, t.me): cw = CallableWrapper(f) self.assertTrue(cw._get_ismethod())
def testClassMethod(self): #{{{ '''Class method gets class soft reference and function hard reference''' class _(object): #{{{ def me(self): #{{{ pass # End def #}}} # End class #}}} cw = CallableWrapper(_.me) self.assertTrue(cw._object) self.assertTrue(isinstance(cw._object, cref)) self.assertEqual(cw._object(), _) self.assertEqual(cw._function, _.me.im_func) self.assertEqual(cw._methodtype, METHODTYPE_CLASS)
def testNormalFunction(self): #{{{ '''Normal functions and unbound methods''' def _(): #{{{ pass # End def #}}} funcs = ((_, METHODTYPE_NOTMETHOD), (method(_, None, None), METHODTYPE_UNBOUND)) for f, mt in funcs: cw = CallableWrapper(f) self.assertTrue(cw._object is None) self.assertTrue(cw._function) self.assertTrue(isinstance(cw._function, cref)) self.assertEqual(cw._function(), f) self.assertEqual(cw._methodtype, mt)
def testMethod(self): #{{{ '''Bound and instance methods''' class _(object): #{{{ def me(self): #{{{ pass # End def #}}} # End class #}}} t1, i_ = _.me, _() t2 = i_.me for f in (t1, t2): cw = CallableWrapper(f) ret = cw._getcallable() self.assertNotEqual(ret, f) self.assertEqual(ret, f.im_func)
def testWrap(self): #{{{ '''If wrapped, will call the wrap function''' def deco(f): #{{{ def w(s): #{{{ return 400 # End def #}}} return w # End def #}}} _ = lambda: 100 cw = CallableWrapper(_) self.assertEqual(cw(), 100) cw.wrap(deco) self.assertEqual(cw(), 400) cw.unwrap() self.assertEqual(cw(), 100)
def testReferences(self): #{{{ '''Live reference (functions always alive)''' t1 = lambda: None class t2(object): #{{{ def me(self): #{{{ pass # End def #}}} # End class #}}} t3 = t2() cw = None for f in (t1, t2.me, t3.me): cw = CallableWrapper(f, weak=True) self.assertEqual(cw._isdead(), False) del t3, f self.assertTrue(cw._isdead())
def testInstanceMethod(self): #{{{ '''Instance method gets instance soft reference and function hard reference''' class _(object): #{{{ def me(self): #{{{ pass # End def #}}} # End class #}}} t = _() cw = CallableWrapper(t.me) self.assertTrue(cw._object) self.assertTrue(isinstance(cw._object, cref)) self.assertEqual(cw._object(), t) self.assertEqual(cw._function, _.me.im_func) self.assertEqual(cw._function, t.me.im_func) self.assertEqual(cw._methodtype, METHODTYPE_INSTANCE)
def testUnWrap(self): # {{{ """Unwrap""" class A(object): def helloworld(self, a, b): return "hello world" a = A() w = CallableWrapper(a.helloworld) w.wrap(DummyReplacement) a.helloworld = method(w, a, A) self.assertEqual(a.helloworld(1, 2), "HELLO: hello world :WORLD") self.assertEqual(A().helloworld(1, 2), "hello world") w.unwrap() self.assertEqual(A().helloworld(1, 2), a.helloworld(1, 2))
def testWrap(self): #{{{ '''Proper wrap functionality given proper input''' cw = CallableWrapper(self.func) cw.wrap(self.deco) self.assertTrue(cw._newcall('a', str)) self.assertFalse(cw._newcall('a', unicode)) self.assertTrue(cw._newcall(u'a', unicode)) self.assertFalse(cw._newcall(1, int))
def __init__(self, signal, **kwargs): #{{{ if not iscallable(signal): raise TypeError("Argument must be callable.") expected = ('weak', 'active', 'activate_on_call') if any(kw for kw in kwargs if kw not in expected): raise ValueError("Detected unexpected arguments: %s" %', '.join([found])) weak = kwargs.get('weak', True) self._func = CallableWrapper(signal, weak=weak) self.__name__ = self._func.__name__ funclist = self._funclist = dict() conn = self._connections = dict() call_funclist = self._call_funclist = dict((n, odict()) for n in ('after', 'replace', 'around', 'before')) self._vars = getattr(self, '_vars', dict()) self._vars.update(active=kwargs.get('active', True), callactivate=kwargs.get('activate_on_call', False), caller=callfunc) # Initialize values of function lists self._init_funclist(funclist) self._init_calls(call_funclist) self._init_connections(conn)
def testClassMethodWrap(self): # {{{ """Wrapping class method""" class A(object): def helloworld(self, a, b): return "hello world" w = CallableWrapper(A.helloworld) w.wrap(DummyReplacement) A.helloworld = method(w, None, A) z = CallableWrapper(A.helloworld) z.wrap(DummyReplacement) A.helloworld = method(z, None, A) a = A() self.assertEqual(a.helloworld(1, 2), "HELLO: HELLO: hello world :WORLD :WORLD")
def testInstanceMethodWrap(self): # {{{ """Wrapping instance method""" class A(object): def helloworld(self, a, b): return "hello world" a = A() w = CallableWrapper(a.helloworld) w.wrap(DummyReplacement) a.helloworld = method(w, a, A) z = CallableWrapper(a.helloworld) z.wrap(DummyReplacement) a.helloworld = method(z, a, A) self.assertEqual(a.helloworld(1, 2), "HELLO: HELLO: hello world :WORLD :WORLD") self.assertEqual(A().helloworld(1, 2), "hello world")
def testMethodWrap(self): #{{{ '''If return value of arg is not a method, makes it into a method''' cw = CallableWrapper(self.func) cw.wrap(self.deco) self.assertTrue(isinstance(cw._newcall, method))
class BaseSignal(object): #{{{ __slots__ = ('__weakref__', '_func', '__name__', '_funclist', '_call_funclist', '_connections', '_vars') def __init__(self, signal, **kwargs): #{{{ if not iscallable(signal): raise TypeError("Argument must be callable.") expected = ('weak', 'active', 'activate_on_call') if any(kw for kw in kwargs if kw not in expected): raise ValueError("Detected unexpected arguments: %s" %', '.join([found])) weak = kwargs.get('weak', True) self._func = CallableWrapper(signal, weak=weak) self.__name__ = self._func.__name__ funclist = self._funclist = dict() conn = self._connections = dict() call_funclist = self._call_funclist = dict((n, odict()) for n in ('after', 'replace', 'around', 'before')) self._vars = getattr(self, '_vars', dict()) self._vars.update(active=kwargs.get('active', True), callactivate=kwargs.get('activate_on_call', False), caller=callfunc) # Initialize values of function lists self._init_funclist(funclist) self._init_calls(call_funclist) self._init_connections(conn) # End def #}}} def _init_funclist_names(self): #{{{ yield 'before' yield 'after' # End def #}}} def _init_funclist(self, funclist): #{{{ init = self._init_funclist_names() funclist.update((name, []) for name in init) # End def #}}} def _init_calls(self, call_funclist): #{{{ cleanlist = self._cleanlist for cftype in ('before', 'replace', 'around', 'after'): call_funclist[cftype].update(getattr(self, '_init_calls_%s' %cftype)(cleanlist).iteritems()) # End def #}}} def _init_calls_before(self, cleanlist): #{{{ def call_before(self, cw, func, ret, args, kwargs): #{{{ callfunc = self.caller for bfunc, t in cleanlist('before'): callfunc(self, bfunc, 'before', False, None, *args, **kwargs) return ret # End def #}}} return odict(before=call_before) # End def #}}} def _init_calls_replace(self, cleanlist): #{{{ return odict() # End def #}}} def _init_calls_around(self, cleanlist): #{{{ return odict() # End def #}}} def _init_calls_after(self, cleanlist): #{{{ def call_after(self, cw, func, ret, args, kwargs): #{{{ callfunc = self.caller for afunc, t in cleanlist('after'): callfunc(self, afunc, 'after', False, None, *args, **kwargs) return ret # End def #}}} return odict(after=call_after) # End def #}}} def _init_default_connections(self): #{{{ init = ('after', 'before') for n in init: yield n # End def #}}} def _init_connections(self, connections): #{{{ init = self._init_default_connections() connections.update((n, (connect_func, disconnect_func)) for n in init) # End def #}}} def __call__(self, *args, **kwargs): #{{{ if not self.valid: warn('Calling an invalid signal', RuntimeWarning, stacklevel=2) return if self.activate_on_call: self.active = True return self._func(*args, **kwargs) # End def #}}} def _cleanlist(self, l): #{{{ # If everything is being deleted e.g. program termination # don't do anything and just return if not hasattr(self, '_funclist'): return l = self._funclist[l] llen, i = len(l), 0 while i < llen: func = l[i] if func.isdead: del l[i] llen -= 1 else: yield func, i i += 1 # End def #}}} def _generate_wrapfactory(self): #{{{ def do_wrap(func): #{{{ def newcall(s, *args, **kwargs): #{{{ ret = None callfunclist = self._call_funclist for name, cfunc in callfunclist['before'].iteritems(): ret = cfunc(self, s, func, ret, args, kwargs) ret = func(*args, **kwargs) for name, cfunc in callfunclist['after'].iteritems(): ret = cfunc(self, s, func, ret, args, kwargs) return ret # End def #}}} return newcall # End def #}}} return do_wrap # End def #}}} def reload(self): #{{{ sfunc = self._func if sfunc.isdead: return sfunc.unwrap() sfunc_wrap = sfunc.wrap callfunclist = self._call_funclist for ctype in ('replace', 'around'): for name, citer in callfunclist[ctype].iteritems(): for cfunc in citer(self): sfunc_wrap(cfunc) do_wrap = self._generate_wrapfactory() sfunc_wrap(do_wrap) self._vars['active'] = True # End def #}}} def _find_cond(self, **kw): #{{{ return lambda s, ln, sl, f, i: True # End def #}}} def _find(self, func, siglistseq=None, **kw): #{{{ temp_siglistseq = self._funclist if siglistseq not in temp_siglistseq: siglistseq = temp_siglistseq else: siglistseq = {siglistseq: temp_siglistseq[siglistseq]} del temp_siglistseq foundindex = foundlist = None fid, cleanlist = cid(func), self._cleanlist fcond = self._find_cond(**kw) for listname, siglist in siglistseq.iteritems(): for f, index in cleanlist(listname): if f.cid == fid: if not fcond(self, listname, siglist, f, index): continue foundindex, foundlist = index, siglist break else: continue break if foundindex is not None: return foundindex, foundlist return None # End def #}}} def connect(self, *after, **other_slots): #{{{ activate = self.activate_on_call self.activate_on_call = False osg = other_slots.get other_slots['after'] = list(after) + list(osg('after', [])) for name, (cfunc, _) in self._connections.iteritems(): cfunc(self, name, other_slots) self.activate_on_call = activate if self.active: self.reload() # End def #}}} def disconnect(self, *after, **other_slots): #{{{ osg = other_slots.get if after or 'after' in other_slots: other_slots['after'] = list(after) + list(osg('after', [])) no_slots = not any(osg(name, None) for name in self._funclist) if 'deleteall' not in other_slots: other_slots['deleteall'] = no_slots for name, (_, dfunc) in self._connections.iteritems(): dfunc(self, name, other_slots) if self.active: self.reload() # End def #}}} def slot(self, name): #{{{ return (f for f, _ in self._cleanlist(name)) # End def #}}} def _setactive(self, v): #{{{ opt = self._vars orig = opt['active'] v = bool(v) opt['active'] = v if orig and not v: self._func.unwrap() elif not orig and v: self.reload() # End def #}}} def _setcallactivate(self, v): #{{{ self._vars['callactivate'] = bool(v) # End def #}}} def _setcaller(self, c): #{{{ if c is None: return elif not iscallable(c): raise TypeError('caller property must be a valid callable object') self._vars['caller'] = CallableWrapper(c, weak=False) # End def #}}} # Properties #{{{ func = property(lambda s: s._func) valid = property(lambda s: not s._func.isdead) connected = property(lambda s: any(s._funclist.itervalues())) active = property(lambda s: s._vars['active'], lambda s, v: s._setactive(v)) activate_on_call = property(lambda s: s._vars['callactivate'], lambda s, v: s._setcallactivate(v)) caller = property(lambda s: s._vars['caller'], lambda s, c: s._setcaller(c)) original = property(lambda s: s._func.original)
def testUnwrap(self): #{{{ '''Unwrapping returns to default call state''' cw = CallableWrapper(self.func) cw.wrap(self.deco) cw.unwrap() self.assertEqual(cw._newcall, cw.call)
def testNonMethodCallable(self): #{{{ '''Non-method callable returns actual callable''' w = CallableWrapper(DummyFunction) self.assert_(w._getcallable() is DummyFunction)
def testNonMethodWrap(self): # {{{ """Wrapping a non-method callable""" w = CallableWrapper(DummyFunction) w.wrap(DummyReplacement) self.assertEqual(w(1, 2), "HELLO: %s :WORLD" % DummyFunction(1, 2))
def testClassInstance(self): #{{{ '''Class method callable's im_func returned''' A = DummyClass w = CallableWrapper(A.DummyMethod) self.assert_(w._getcallable() is A.DummyMethod.im_func)
def testMethodInstance(self): #{{{ '''Instance method callable's im_func returned''' A = DummyClass a = A() w = CallableWrapper(a.DummyMethod) self.assert_(w._getcallable() is a.DummyMethod.im_func)