[Twisted-Python] Bug in t.w.w.WidgetPage

Sune Kirkeby sune-twisted at mel.interspace.dk
Wed Jan 2 17:37:08 EST 2002


[ Sune Kirkeby ]
> And here is a patch to fix it.

s/WidgetPage/RenderSession/g, in my previous two mails.

i-need-a-mua-that-reads-my-mind-and-attach-patches'ly,

-- 
Sune Kirkeby | Your superior intellect is no match for
             | our puny weapons.
-------------- next part --------------
diff --exclude-from=diffignore -Naur Twisted-CVS/twisted/web/widgets.py Twisted/twisted/web/widgets.py
--- Twisted-CVS/twisted/web/widgets.py	Wed Dec 12 13:00:14 2001
+++ Twisted/twisted/web/widgets.py	Wed Jan  2 23:17:53 2002
@@ -474,72 +474,122 @@
         return html.PRE(io.getvalue())
 
 class RenderSession:
+    """I handle rendering of a list of deferreds, outputting their
+    results in correct order."""
+
+    class Sentinel:
+        pass
+
     def __init__(self, lst, request):
         self.lst = lst
         self.request = request
-        self.position = 0
         self.needsHeaders = 0
-        pos = 0
+        self.beforeBody = 1
+
         toArm = []
-        # You might want to set a header from a deferred, in which case you
-        # have to set an attribute -- needsHeader.
-        for item in lst:
+        for i in range(len(self.lst)):
+            item = self.lst[i]
             if isinstance(item, defer.Deferred):
-                self._addDeferred(item, pos)
+                self.lst[i] = (self._addDeferred(item), item)
                 toArm.append(item)
-            pos = pos + 1
+    
         self.keepRendering()
-        # print "RENDER: DONE WITH INITIAL PASS"
         for item in toArm:
             item.arm()
 
-    def _addDeferred(self, deferred, position):
+    def _addDeferred(self, deferred):
+        sentinel = self.Sentinel()
         if hasattr(deferred, 'needsHeader'):
+            # You might want to set a header from a deferred, in which
+            # case you have to set an attribute -- needsHeader.
             self.needsHeaders = self.needsHeaders + 1
-            args = (position, 1)
+            args = (sentinel, 1)
         else:
-            args = (position, 0)
+            args = (sentinel, 0)
         deferred.addCallbacks(self.callback, self.callback,
                               callbackArgs=args, errbackArgs=args)
+        return sentinel
 
-    def callback(self, result, position, decNeedsHeaders):
+    def callback(self, result, sentinel, 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):
+        
+        # Make sure result is a sequence,
+        if not type(result) in (types.ListType, types.TupleType):
             result = [result]
-        self.lst[position:position+1] = result
-        assert self.position <= position
+
+        # If the deferred does not wish to produce it's result all at
+        # once, it can give us a partial result as
+        #  (NOT_DONE_YET, partial_result)
+        if result[0] is NOT_DONE_YET:
+            done = 0
+            result = result[1]
+            if not type(result) in (types.ListType, types.TupleType):
+                result = [result]
+        else:
+            done = 1
+
+        toArm = []
+        for i in xrange(len(result)):
+            item = result[i]
+            if isinstance(item, defer.Deferred):
+                result[i] = (self._addDeferred(item), item)
+                toArm.append(item)
+
+        for position in range(len(self.lst)):
+            item = self.lst[position]
+            if type(item) is types.TupleType and len(item) > 0:
+                if item[0] is sentinel:
+                    break
+        else:
+            raise 'Sentinel for Deferred not found!'
+
+        if done:
+            self.lst[position:position+1] = result
+        else:
+            self.lst[position:position] = result
+
+        # Consolidate strings to minimize length of list,
+        for i in range(1, len(self.lst)):
+            last = type(self.lst[i-1]) is types.StringType
+            this = type(self.lst[i]) is types.StringType
+            if last and this:
+                self.lst[i-1:i+1] = [self.lst[i-1] + self.lst[i]]
+
         self.keepRendering()
-        for r in result:
-            if isinstance(r, defer.Deferred):
-                r.arm()
+        for r in toArm:
+            r.arm()
 
 
     def keepRendering(self):
         if self.needsHeaders:
-            # short circuit actual rendering process until we're sure no more
-            # deferreds need to set headers...
+            # short circuit actual rendering process until we're sure no
+            # more deferreds need to set headers...
             return
+
         assert self.lst is not None, "This shouldn't happen."
         while 1:
-            item = self.lst[self.position]
-            if self.position == 0 and item == FORGET_IT:
-                # If I haven't moved yet, and the widget wants to take over the page, let it do so!
+            item = self.lst[0]
+            if self.beforeBody and item == FORGET_IT:
+                # If I haven't moved yet, and the widget wants to take
+                # over the page, let it do so!
                 return
+            
             if isinstance(item, types.StringType):
+                self.beforeBody = 0
                 self.request.write(item)
-            elif isinstance(item, defer.Deferred):
-                return
+            elif type(item) is types.TupleType and len(item) > 0:
+                if isinstance(item[0], self.Sentinel):
+                    return
             else:
-                self.request.write("RENDERING UNKNOWN: %s" % html.PRE(repr(item)))
-            self.position = self.position + 1
-            if self.position == len(self.lst):
+                self.beforeBody = 0
+                unknown = html.PRE(repr(item))
+                self.request.write("RENDERING UNKNOWN: %s" % unknown)
+
+            del self.lst[0]
+            if len(self.lst) == 0:
                 self.lst = None
                 self.request.finish()
                 return


More information about the Twisted-Python mailing list