From 410eb9f33029fd2a99d01348b80cbb6ff1aab65e Mon Sep 17 00:00:00 2001 From: John-Mark Gurney Date: Wed, 5 Jul 2006 23:06:29 -0800 Subject: [PATCH] add support for poor many's async... I don't want to tangle out how to handle making Browse async, so I'll just restart the call if a subfunction raises a Deferred object... This should work amazingly well... we may end up wrapping just the check/doUpdate calls... [git-p4: depot-paths = "//depot/": change = 823] --- ContentDirectory.py | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/ContentDirectory.py b/ContentDirectory.py index 91a47cb..836a6ab 100644 --- a/ContentDirectory.py +++ b/ContentDirectory.py @@ -29,9 +29,54 @@ from elementtree.ElementTree import Element, SubElement, tostring from upnp import UPnPPublisher, errorCode from DIDLLite import DIDLElement, Container, Movie, Resource, MusicTrack +from twisted.internet import defer +from twisted.python import failure + import traceback from urllib import quote +class doRecall(defer.Deferred): + '''A class that will upon any callback from the Deferred object passed + in, recall fun(*args, **kwargs), just as if a maybeDeferred has been + processed. + + The idea is to let something deeper called by something sync "abort" + the call until it's ready, and then reattempt. This isn't the best + method as we throw away work, but it can be easier to implement. + + Example: + def wrapper(five): + try: + return doacall(five) + except defer.Deferred, x: + return doRecallgen(x, wrapper, five) + + If doacall works, everything is fine, but if a Deferred object is + raised, we put it in a doRecall class and return the deferred object + generated by doRecall.''' + + def __init__(self, argdef, fun, *args, **kwargs): + self.fun = fun + self.args = args + self.kwargs = kwargs + self.defer = defer.Deferred() + + argdef.addCallback(self._done) + + def _done(self, *args, **kwargs): + ret = self.fun(*self.args, **self.kwargs) + if isinstance(ret, failure.Failure): + self.defer.errback(ret) + elif isinstance(ret, defer.Deferred): + # We are fruther delayed, continue. + ret.addCallback(self._done) + else: + self.defer.callback(ret) + +def doRecallgen(defer, fun, *args, **kwargs): + i = doRecall(defer, fun, *args, **kwargs) + return i.defer + class ContentDirectoryControl(UPnPPublisher, dict): """This class implements the CDS actions over SOAP.""" @@ -123,6 +168,12 @@ class ContentDirectoryControl(UPnPPublisher, dict): BrowseFlags = ('BrowseMetaData', 'BrowseDirectChildren') def soap_Browse(self, *args): + try: + return self.thereal_soap_Browse(*args) + except defer.Deferred, x: + return doRecallgen(x, self.soap_Browse, *args) + + def thereal_soap_Browse(self, *args): """Required: Incrementally browse the native heirachy of the Content Directory objects exposed by the Content Directory Service.""" @@ -151,8 +202,14 @@ class ContentDirectoryControl(UPnPPublisher, dict): # filter out the ones that don't exist anymore, we need # to check against None, since some dirs might be empty # (of valid content) but exist. + # XXX - technically if list changed, we need to get + # some new ones by looping till we have a complete + # list. ochup = filter(lambda x, s = self: s[x.id].checkUpdate() is not None, ch) + # XXX - are we suppose to be calling addContainer + # for Containers instead of always addItem? + #log.msg('ochup:', `ochup`) filter(lambda x, d = didl: d.addItem(x) and None, ochup) total = len(self.getchildren(ObjectID)) else: