josh-modpython at valleyfree.com
josh-modpython at valleyfree.com
Wed Nov 24 16:03:51 EST 2004
Hello folks I'm new to mod_python (trying to migrate from php) and, being used to php, have liked php's output buffering features. Specifically what I was looking for was a way to buffer all output and still control it (clear it inside an error handler, run it through a filter, etc.) Initially this is easy to acheive just using a StringIO object, but when I began using PSP I found this no longer worked, since PSP outputs to the request object directly, and once output is sent to the request object I saw no way to "take it back" or modify it. So, for all the people in my boat, here is some code that will allow you do just that. This comes in very handy if you expect to have errors occur after or during a psp template is run, or if you want to do something like filter the final output. I would appreciate it if any mod_python gurus could point out any pitfalls in this approach. Thanks for any feedback, and I hope this helps a few people out there. (I assume this is kosher to post code straight to the list, if not please slap my hand and inform me of list protocol) import sys import cStringIO import traceback from mod_python import apache from mod_python import psp class bufferProxy: """This proxy class is used to supply a write() method to replace req.write(), including the optional flush parameter, which is ignored and only present to prevent an exception being raised for its presence. If it weren't for the flush parameter, you wouldn't need this proxy class at all, you could just assign req.write to the StringIO object's write method.""" def __init__(self,outputBuffer): self.outputBuffer = outputBuffer def write(self,data,flush=None): """Writes data to the output buffer. The flush argument is simply ignored; the buffer is only flushed at the end of the handler in the finally clause. Though, you could allow flushing by sending the contents of the buffer to the oldReqWrite method and then clearing it.""" self.outputBuffer.write(data) def handler(req): """A request handler that illustrates the use of a StringIO output buffer instead of using the Apache buffer normally written to by req.write, which allows for simultaneous use of all of the following: 1. print statements (redirecting sys.stdout) 2. psp templates (that use req.write) 3. error handlers that can erase/override/filter all output even after it has been written to the buffer """ req.content_type = "text/html" outputBuffer = cStringIO.StringIO() outputBufferProxy = bufferProxy(outputBuffer) oldStdout = sys.stdout oldReqWrite = req.write sys.stdout = outputBuffer req.write = outputBufferProxy.write try: # outer try block used for finally clause try: # inner block used for exception handling # this works print 'Hello world!<br>' # and this works req.write('Hello, again!<br>\n') # and PSP works, too, just put a real psp # template in there and uncomment it, of course # also placing any needed vars in run()... # template = psp.PSP(req, \ # filename='mytemplate.psp') # template.run() # raise and catch the an exception or do error # handling however you like... raise "some error" except: # on error, clear the buffer and show an error, # do stuff, show a stack trace, etc, you can # even show everything output up to this point: allOutputUpToNow = outputBuffer.getvalue() outputBuffer.seek(0) outputBuffer.truncate(0) print '<h1>An exception has occurred...</h1>' print '<pre>' traceback.print_exc(None,outputBuffer) print '</pre>' print '<hr>' print '<h2>Here is all the output up to the point' \ ' of the exception:</h2>' print allOutputUpToNow # note that it would be a good idea to html # entity quote the contents of allOutputUpToNow finally: # restore IO streams and send final output sys.stdout = oldStdout req.write = oldReqWrite req.write(outputBuffer.getvalue()) outputBuffer.close() return apache.OK
|