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 group import Group
from bot import Bot
import deferred
from yowsupwrapper import YowsupApp
from functools import partial
class MsgIDs:
@ -723,23 +725,34 @@ class Session(YowsupApp):
self.buddies.remove(buddy)
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]:
newbuddy = self.legacyName
else:
newbuddy = buddy
self.logger.debug('Requesting profile picture of %s', newbuddy)
self.requestProfilePicture(newbuddy, onSuccess = onSuccess)
buddy = self.legacyName
# Get profile picture
self.logger.debug('Requesting profile picture of %s', buddy)
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):
self.logger.info("Success: Image downloaded to %s", path)