[Buildbot-commits] buildbot/buildbot/status html.py,1.44,1.45 builder.py,1.44,1.45

Brian Warner warner at users.sourceforge.net
Wed Nov 24 02:41:24 UTC 2004


Update of /cvsroot/buildbot/buildbot/buildbot/status
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8495/buildbot/status

Modified Files:
	html.py builder.py 
Log Message:
* buildbot/status/html.py (TextLog): split render() up into
render_HEAD and render_GET. Use a Producer when sending log
chunks, to reduce memory requirements and avoid sending huge
non-Banana-able strings over web.distrib connections. Requires
peeking under the covers of IStatusLog.

* buildbot/status/builder.py (HTMLLogFile.waitUntilFinished): oops,
use defer.succeed, not the non-existent defer.success
(LogFile.waitUntilFinished): same
(LogFile.subscribe): don't add watchers to a finished logfile


Index: builder.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/builder.py,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -d -r1.44 -r1.45
--- builder.py	23 Nov 2004 10:51:03 -0000	1.44
+++ builder.py	24 Nov 2004 02:41:22 -0000	1.45
@@ -98,7 +98,7 @@
         return self.finished
     def waitUntilFinished(self):
         if self.finished:
-            d = defer.success(self)
+            d = defer.succeed(self)
         else:
             d = defer.Deferred()
             self.finishedWatchers.append(d)
@@ -113,6 +113,8 @@
         return self.entries + self.runEntries
 
     def subscribe(self, receiver, catchup):
+        if self.finished:
+            return
         self.watchers.append(receiver)
         if catchup:
             for channel, text in self.entries + self.runEntries:
@@ -213,7 +215,7 @@
     def isFinished(self):
         return True
     def waitUntilFinished(self):
-        return defer.success(self)
+        return defer.succeed(self)
 
     def getText(self):
         return self.html # looks kinda like text

Index: html.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/html.py,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -d -r1.44 -r1.45
--- html.py	23 Nov 2004 03:58:15 -0000	1.44
+++ html.py	24 Nov 2004 02:41:21 -0000	1.45
@@ -456,12 +456,13 @@
 """
 
 class TextLog(Resource):
+    # a new instance of this Resource is created for each client who views
+    # it, so we can afford to track the request in the Resource.
     __implements__ = IHTMLLog,
 
     def __init__(self, original):
         Resource.__init__(self)
         self.original = original
-        self.watchers = []
 
     def htmlHeader(self, request):
         title = "Log File contents"
@@ -491,70 +492,72 @@
         data += "</body></html>\n"
         return data
 
-    def render(self, request):
+    def render_HEAD(self, request):
         asText = request.args.get("text", None)
-
         if asText:
             request.setHeader("content-type", "text/plain")
         else:
             request.setHeader("content-type", "text/html")
 
-        if request.method == "HEAD":
-            # vague approximation, ignores markup
-            request.setHeader("content-length", self.original.length)
-            return ''
-
-        data = ""
-        if not asText:
-            data += self.htmlHeader(request)
-        data += self.content(self.original.getChunks(), asText)
+        # vague approximation, ignores markup
+        request.setHeader("content-length", self.original.length)
+        return ''
 
-        # TODO: to implement a producer for this:
-        #  Create an object that knows about the LogFile. That object's
-        #  .resumeProducing method should pull text from the LogFile's
-        #  .entries, convert them to HTML, then send them to request.write .
-        #  (remember that .entries may be added while the request is being
-        #  served). When all .entries are consumed, send everything in
-        #  .runEntries (oh and do those all before they get merged into a
-        #  new .entries item). Then attach the request to our .watchers
-        #  method and make sure the LogFile knows about us.
+    def resumeProducing(self):
+        if self.chunkNumber < len(self.original.entries):
+            chunk = self.original.entries[self.chunkNumber]
+            self.chunkNumber += 1
+            self.req.write(self.content([chunk], self.asText))
+            return
+        # now send all of .runEntries in a batch
+        data = self.content(self.original.runEntries, self.asText)
+        self.req.write(data)
+        self.req.unregisterProducer()
+        # then see if there is more to come
+        self.original.subscribe(self, False)
+        d = self.original.waitUntilFinished()
+        d.addCallback(self.finished)
 
-        if self.original.finished:
-            if not asText:
-                data += self.htmlFooter()
-            return data # all done
+    def render_GET(self, req):
+        self.req = req
+        self.asText = self.req.args.get("text", None)
 
-        try:
-            request.write(data)
-        except pb.DeadReferenceError:
-            # master server died awfully early
-            return "you're never going to hear this, are you"
+        if self.asText:
+            req.setHeader("content-type", "text/plain")
+        else:
+            req.setHeader("content-type", "text/html")
 
-        self.watchers.append((request, asText))
-        self.original.subscribe(self, False)
-        self.original.waitUntilFinished().addCallback(self.finished)
+        if not self.asText:
+            req.write(self.htmlHeader(req))
 
-        d = request.notifyFinish()
-        d.addErrback(lambda why: self.watchers.remove((request, asText)))
+        self.chunkNumber = 0
+        req.registerProducer(self, False)
+        d = req.notifyFinish()
+        d.addErrback(self.stop)
         return server.NOT_DONE_YET
 
+    def stop(self, why):
+        self.original.unsubscribe(self)
+        self.req.unregisterProducer()
+        # our .finished callback may still be fired
+        self.req = None
+
     def logChunk(self, build, step, log, channel, text):
-        for request, asText in self.watchers[:]:
-            output = self.content([(channel, text)], asText)
-            try:
-                request.write(output)
-            except pb.DeadReferenceError:
-                self.watchers.remove((request, asText))
+        output = self.content([(channel, text)], self.asText)
+        try:
+            self.req.write(output)
+        except pb.DeadReferenceError:
+            log.unsubscribe(self)
 
     def finished(self, log):
-        for request, asText in self.watchers:
-            try:
-                if not asText:
-                    request.write(self.htmlFooter())
-                request.finish()
-            except pb.DeadReferenceError:
-                pass
-        self.watchers = []
+        if not self.req:
+            return
+        try:
+            if not self.asText:
+                self.req.write(self.htmlFooter())
+            self.req.finish()
+        except pb.DeadReferenceError:
+            pass
 
 components.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog)
 





More information about the Commits mailing list