I think there might be a bug lurking in t.w.w.WidgetPage's handling of Deferreds. Take a look at t.w.w.WidgetPage.callback:
def callback(self, result, position, decNeedsHeaders): if result != FORGET_IT: self.needsHeaders = self.needsHeaders - decNeedsHeaders else: result = [FORGET_IT] for i in xrange(len(result)): if isinstance(result[i], defer.Deferred): self._addDeferred(result[i], position+i) # print 'CALLBACK:',self.lst, position, result if not isinstance(result, types.ListType): result = [result] self.lst[position:position+1] = result assert self.position <= position self.keepRendering() for r in result: if isinstance(r, defer.Deferred): r.arm()
Now consider this sequence of events:
(1) We initialize a t.w.w.WidgetPage with two Deferreds (head and foot), so in our WidgetPage (let us call it foo) we have foo.lst = [head, foot].
(2) mouth is now almost done, so it defers the rest of it's computation, and callback's with a string 'Hello, ' and a Deferred (let us call it mouth).
(3) After foo.callback is done we now have that foo.lst = ['Hello, ', mouth, foot].
(4) Now foot is done and callback's with a string 'World!'.
(5) After foo.callback is done this time we have that foo.lst = ['Hello, ', 'World!', foot]
Whoa! We just put our foot in our mouth! So to speak :-)