views:

300

answers:

2

This is a bit of a tricky question;

I'm working with mod_wsgi in python and want to make an output buffer that yields HTML on an ongoing basis (until the page is done loading).

Right now I have my script set up so that the Application() function creates a separate 'Page' thread for the page code, then immediately after, it runs a continuous loop for the Output Buffer using python's Queue lib.

Is there a better way to have this set up? I thought about making the Output Buffer be the thread (instead of Page), but the problem with that, is the Application() function is the only function that can be yielding the HTML to Apache, which (as far as I can tell, makes this idea impossible).

The downside I'm seeing with my current setup, is in the event of an error, I can't easily interrupt the buffer and exit without the Page thread continuing on for a bit.

(It kinda sucks that mod_wsgi doesn't have a build in output buffer to handle this, I hate loading the entire page then sending output just once, it results in a much slower page load).

+2  A: 

mod_wsgi should have built in support for Generators. So if your using a Framework like CherryPy you just need to do:

def index():
    yield "Some output"
    #Do Somemore work
    yield "Some more output"

Where each yield will return to the user a chunk of the page.

Here is some basics from CherrPy on there implementation and how it works http://www.cherrypy.org/wiki/ReturnVsYield

NerdyNick
That's actually exactly what I'm doing, but in order to make the code clean i need to make an output buffer so it only yields output once a large enough amount of html has piled up. Yielding every time there's something to send slows down apache.
Ian
+1  A: 

(It kinda sucks that mod_wsgi doesn't have a build in output buffer to handle this, I hate loading the entire page then sending output just once, it results in a much slower page load).

Unless you're doing some kind of streaming or asynchronous application, you want to send the entire page all at once 99.9% of the time. The only exception that I can think of is if you're sending a big webpage (and by big, I mean in the hundreds of Megabytes).

The reason why I mention this is to point out that if you're having performance problems, it's likely not because you're buffering output. The simplest way to handle this is to do something like this:

def Application(environ, start_response):
    start_response('200 Ok', [('Content-type','text/plain')])
    response = []
    response.append('<h1>')
    response.append('hello, world!')
    response.append('</h1>')
    return [''.join(response)] #returns ['<h1>hello, world!</h1>']

Your best bet is to use a mutable data structure like a list to hold the chunks of the message and then join them together in a string as I did above. Unless you have some kind of special need, this is probably the best general approach.

Jason Baker
I don't like this approach since sending chunked websites always load faster since you're not waiting for the page to be done compiling before the user can start receiving it. The net result is a trimmed down load time.
Ian
It also makes development more annoying since an error results in zero output (making debugging very tricky).
Ian
@Ian - The thing is that the page will be chunked by TCP/IP anyway. It *does* make sense to chunk the page if it will take a long time to generate, but most pages won't. In most cases, you can literally generate the page thousands if not millions or billions of times before one page is sent over the network. The same is true if you're querying a database to generate the page. In other words, any speed advantage that you would get would be negligable. Thus, you're better doing the simplest thing.
Jason Baker
I'm starting to think I'll have to do this, but I would still much prefer to do things in the a chunked fashion since it leaves me with more options in the future.
Ian
@Ian - I'm a believer that YAGNI is an oversimplification, but I feel that it applies in this case: http://en.wikipedia.org/wiki/You_Ain't_Gonna_Need_It
Jason Baker