|
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
|