On May 14, 2009, at 4:01 AM, Terry Jones wrote:
"Phil" == Phil Christensen <[email protected]> writes:
Phil> I don't know if I agree with the need for such a feature (that
is,
Phil> deferred __init__ usage), but it was a very interesting coding
Phil> challenge I wanted to take a whack at. I *think* I might have
found a
Phil> solution, but I don't know if it falls under the heading of
Phil> "decorator abuse" ;-)
Hi Phil
I finally had time to look at your solution a bit (though I've not run
it). It does a couple of things I wouldn't have thought of, like
putting
the dictionary onto the deferredInit function. A couple of comments,
supposing I understand your code properly:
- One thing I had hoped to avoid was to slow the class methods down by
having them always check the original deferred (or a flag) before
taking
action. My approach does this by moving them aside and then
putting them
back in place once the deferred fires. Your solution requires that
every
decorated method does several extra things before it gets going.
That
could be greatly reduced if you were to check
self.initDeferred.called
and simply call the original function if the deferred has fired.
Yeah, I see what you mean. I changed it to directly call the function
if the initDeferred has already been fired. One catch is that I think
it's important that the function always return a Deferred, even if
it's just a succeed() wrapper, so as to provide a consistent interface
whether __init__ is finished or not.
I added a couple additional calls to my test example to illustrate this.
- If multiple calls are made to instance methods before the init
deferred
has fired, they will, as I read it, all try to del
deferredInit.waiting[self] in _finish. So I guess that del needs
to be
conditional or in a try/except.
I realized there's no reason to keep a dictionary anyways, since you
always have access to `self`. The result means less bookkeeping, which
is always good...
- Using self as a key into the dict on initDeferred seems like it
addresses Glyph's observation/criticism that my approach raises
questions wrt inheritance.
I believe even though I removed that state dictionary, this should
still work properly, since we always operate on/with `self`.
- You could use chainDeferred where you're currently using
.addCallbacks(resultDeferred.callback, resultDeferred.errback)
Ah yes. I forgot this existed; a lot of my Deferred experience is with
sequential processes, so I've been using inlineCallbacks for everything.
That's all for now. I'll see if I have more time to think about all
this.
When I tried to use a decorator the first time, I was also using a
super
class (whereas you're putting state into a dict on the deferredInit
function) but I got into a mess accessing self properly (partly
because, I
think, I wanted to have a mixin class and I was looking at
self.__class__.__mro__).
In any case, thanks for replying, for playing with it, and for
posting your
code. I got to learn new things as a result, which is really great :-)
Yeah, same here. It's pretty rare that I can get into any 'semi-
advanced' discussions on here (things often seem to go between one
extreme and the other), but I always learn lots when I do.
I'm still not sure if I would use this technique myself, but I'm
reasonably satisfied with the "scent" of this code. Obviously though,
I haven't thought through a whole bunch of use cases, but it seems to
be pretty simple, in the end.
I've attached the revised version of the decorator. Let me know if you
think of anything else.
Thanks,
-phil
from twisted.internet import defer, reactor
from twisted.enterprise import adbapi
def deferredInit(deferredName):
def _deferredInit(func):
def __deferredInit(self, *args, **kwargs):
initDeferred = None
if(hasattr(self, deferredName)):
initDeferred = getattr(self, deferredName)
if(initDeferred.called):
return defer.maybeDeferred(func, self, *args, **kwargs)
else:
raise RuntimeError("%s doesn't define the Deferred attribute `%s`." % (self.__class__.__name__, deferredName))
def _finish(result):
return func(self, *args, **kwargs)
def _finish_error(failure):
print '_finish_err: %s' % failure
resultDeferred = defer.Deferred()
resultDeferred.addCallbacks(_finish, _finish_error)
initDeferred.chainDeferred(resultDeferred)
return resultDeferred
return __deferredInit
if(callable(deferredName)):
func = deferredName
deferredName = 'initDeferred'
return _deferredInit(func)
return _deferredInit
class TestDeferredInit(object):
def __init__(self):
self.pool = adbapi.ConnectionPool("MySQLdb", 'localhost', 'modu', 'modu')
self.initDeferred = self.pool.runQuery("SELECT 'it worked';")
def _finish_init(msg):
self.msg = msg
def _finish_init_error(failure):
print '_finish_init_err: %s' % failure
self.initDeferred.addCallbacks(_finish_init, _finish_init_error)
@deferredInit
def query(self):
return self.msg
if(__name__ == '__main__'):
def _print(msg, label):
print '%s: %s' % (label, msg)
if(label == '3'):
reactor.stop()
def _print_error(failure):
print '_print_err: %s' % failure
test = TestDeferredInit()
d = test.query()
d.addCallbacks(_print, _print_error, callbackArgs=['1'])
d2 = test.query()
d2.addCallbacks(_print, _print_error, callbackArgs=['2'])
d3 = test.query()
d3.addCallbacks(_print, _print_error, callbackArgs=['3'])
reactor.run()
_______________________________________________
Twisted-Python mailing list
[email protected]
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python