Add Deferreds

This commit is contained in:
moyamo 2016-01-01 17:31:17 +02:00
parent 5fb84bca4f
commit 03e56a4c07
2 changed files with 126 additions and 16 deletions

97
deferred.py Normal file
View file

@ -0,0 +1,97 @@
class Deferred(object):
"""
Represents a delayed computation. This is a more elegant way to deal with
callbacks.
A Deferred object can be thought of as a computation whose value is yet to
be determined. We can manipulate the Deferred as if it where a regular
value by using the then method. Computations dependent on the Deferred will
only proceed when the run method is called.
Attributes of a Deferred can be accessed directly as methods.
Example:
image = Deferred()
getImageWithCallback(image.run)
image.then(displayFunc)
colors = Deferred()
colors.append('blue')
colors.then(print)
colors.run(['red', 'green']) #=> ['red', 'green', 'blue']
"""
def __init__(self):
self.subscribers = []
self.computed = False
self.args = None
self.kwargs = None
def run(self, *args, **kwargs):
"""
Give a value to the deferred. Calling this method more than once will
result in a DeferredHasValue exception to be raised.
"""
if self.computed:
raise DeferredHasValue("Deferred object already has a value.")
else:
self.args = args
self.kwargs = kwargs
for func, deferred in self.subscribers:
deferred.run(func(*args, **kwargs))
self.computed = True
def then(self, func):
"""
Apply func to Deferred value. Returns a Deferred whose value will be
the result of applying func.
"""
result = Deferred()
if self.computed:
result.run(func(*self.args, **self.kwargs))
else:
self.subscribers.append((func, result))
return result
def arg(self, n):
"""
Returns the nth positional argument of a deferred as a deferred
Args:
n - the index of the positional argument
"""
def helper(*args, **kwargs):
return args[n]
return self.then(helper)
def __getattr__(self, method_name):
return getattr(Then(self), method_name)
class Then(object):
"""
Allows you to call methods on a Deferred.
Example:
colors = Deferred()
Then(colors).append('blue')
colors.run(['red', 'green'])
colors.then(print) #=> ['red', 'green', 'blue']
"""
def __init__(self, deferred):
self.deferred = deferred
def __getattr__(self, name):
def tryCall(obj, *args, **kwargs):
if callable(obj):
return obj(*args, **kwargs)
else:
return obj
def helper(*args, **kwargs):
func = (lambda x: tryCall(getattr(x, name), *args, **kwargs))
return self.deferred.then(func)
return helper
class DeferredHasValue(Exception):
def __init__(self, string):
super(DeferredHasValue, self).__init__(string)

View file

@ -39,7 +39,9 @@ from buddy import BuddyList
from threading import Timer from threading import Timer
from group import Group from group import Group
from bot import Bot from bot import Bot
import deferred
from yowsupwrapper import YowsupApp from yowsupwrapper import YowsupApp
from functools import partial
class MsgIDs: class MsgIDs:
@ -723,23 +725,34 @@ class Session(YowsupApp):
self.buddies.remove(buddy) self.buddies.remove(buddy)
def requestVCard(self, buddy, ID=None): def requestVCard(self, buddy, ID=None):
def onSuccess(response, request):
self.logger.debug('Sending VCard (%s) with image id %s',
ID, response.pictureId)
image_hash = utils.sha1hash(response.pictureData)
self.logger.debug('Image hash is %s', image_hash)
if ID != None:
self.backend.handleVCard(self.user, ID, buddy, "", "", response.pictureData)
if not (buddy == self.user or buddy == self.user.split('@')[0]):
obuddy = self.buddies[buddy]
self.updateBuddy(buddy, obuddy.nick, obuddy.groups, image_hash)
if buddy == self.user or buddy == self.user.split('@')[0]: if buddy == self.user or buddy == self.user.split('@')[0]:
newbuddy = self.legacyName buddy = self.legacyName
else:
newbuddy = buddy # Get profile picture
self.logger.debug('Requesting profile picture of %s', newbuddy) self.logger.debug('Requesting profile picture of %s', buddy)
self.requestProfilePicture(newbuddy, onSuccess = onSuccess) response = deferred.Deferred()
self.requestProfilePicture(buddy, onSuccess = response.run)
response = response.arg(0)
# Send VCard
if ID != None:
response.pictureId().then(partial(
self.logger.debug, 'Sending VCard (%s) with image id %s', ID
))
pictureData = response.pictureData()
response.pictureData().then(partial(
self.backend.handleVCard, self.user, ID, buddy, "", ""
))
# Send image hash
if not buddy == self.legacyName:
obuddy = self.buddies[buddy]
image_hash = pictureData.then(utils.sha1hash)
image_hash.then(partial(self.logger.debug, 'Image hash is %s'))
image_hash.then(partial(
self.updateBuddy, buddy, obuddy.nick, obuddy.groups
))
def onDlsuccess(self, path): def onDlsuccess(self, path):
self.logger.info("Success: Image downloaded to %s", path) self.logger.info("Success: Image downloaded to %s", path)