My question is about how to design code that works well with object oriented design and asynchronous deferreds (instead of blocking code)
Ok two ways I am thinking about designing the class (are any of these good designs or am I forgetting something)
First Way
class Grooveshark(object):
def get_session(self):
d = # implementation detail (gets page)
d.addCallback(self.parse_session)# implmentation detail
# all in all this goes through and sets self.session to the session value (it does not return it though; should I set it and return it?)
self.session_time = time.time()
return d
def get_country_id(self):
# implmentation acts same as d just parses to diferrent id
# also it grabs the same page; ANNOYING having to get the page twice ugh
def get_token(self):
# relies on self.session being set
d = # implmentation detail
d.addCallback(self.parse_token)# implmentation detail
# at the end of the day it sets self.token and then fires the deferred (same as session does not send it through the deferred, again should I seem repetitive?)
return d
def construct_api_call(method, url, implmentation_arguments...)
# checks if session is hour old
if self.session_time - 3600 <= time.time() or self.session is None:
# update
d = get_session()
# some how pass deferred or something
d.addCallback(lambda ignored: self.get_country_id)
d.addCallback(lambda ignored: self.get_token())
d.addCallback(lambda ignored: self.construct_api_call(method, url, implmentation_arguments)
# if error retry
d.addErrback(lambda error: log.err(error))
d.addErrback(lambda ignored: self.construct_api_call(method, url, implmentation_arguments)
return d# would this work? problem: with this how do I get it so I can do this whole update and do this again with the deferred I returned
else:
#implmentation details
return d# fires when done with api call
Second Way
class Grooveshark(object):
def get_session(self):
d = # implmentation detail
# differance this sends the session down the deferred callback and sets the instance var self.session (seems strange both modifying state and returning)
def get_token(self, session):
d = # gets token but uses session argument NOT intance variable
def get_country_id # same as first class
def construct_api_call(session, session_time, token, country_id, all the other args in above class):
# problems it requires user of api to store session etc also how do I return if needs update right now I just error
if self.session_time - 3600 <= time.time():
raise RuntimeError("You need to update your session, ugh why does the user have to store the session ugh")
else:
# does what above class does as well
Short Answer: See @defer.inlineCallbacks
Regardless of functional or object-oriented programming the crux and blessing of using Twisted is that it uses a callback event-driven design to allow asynchronous program execution. A common observation is that event driven programming requires a shift in coding style and layout – as your question indicates.
In almost all circumstances using the “@defer.inlineCallbacks” decorator in your methods or functions will help make your twisted code modular and re-usable. When you write source code using this methodology you can write asynchronous code that isn’t “split up” between so many functions. Each time your code-block needs to go onto the next blocking or ‘deferred’ segment it uses the yield command. This allows the function to continue where it left off when the deferred finishes. This makes a chain of callbacks appear like regular blocking code.
More Help