[mod_python] Capturing PSP output

Graham Dumpleton grahamd at dscpl.com.au
Wed Jun 1 02:31:38 EDT 2005


Nicolas Lehuen wrote ..
> The problem with this is that if the PSP needs to access some request
> variables (like form variables), then it will fail.

Agreed, and why I pointed out request object was being replaced
completely with dummy object. ;-)

> I think the best
> way is still to decorate the request object with a fake write method,
> something like this :
> 
> # this is pseudo code...
> buffer = StringIO()
> real_write = req.write
> req.write = buffer.write
> try:
>     # do the PSP trick
> finally:
>     req.write = real_write

This will not work as written as req.write() takes an optional second argument
to indicate whether output should be flushed. The StringIO.write() doesn't
have an equivalent second argument and the generated PSP code explicitly
sets the second argument of the write() call to 0. Thus PSP writing direct to
StringIO.write() will cause a failure. You could do it with an extra level of
indirection though which is what I was doing by deriving from the StringIO
class in the first place to override write(). I think you probably realise that
as later suggestion has extra level of indirection.

> Another method could be to use a delegating request object, something like
> :
> 
> class FakeRequest(object):
>     def __init__(self,req):
>         self.req = req
>         self.buffer = StringIO()
> 
>     def write(self,string,flush=1):
>         self.buffer.write(string)
> 
>     def __getattr__(self,name):
>         return getattr(self.req,name)

In some ways this is nicer, but also has problems in that it cannot entirely
replace the original request object because of type checking within the C
code in mod_python.

For example, if using the fake request object and you wrote something like:

  req.register_cleanup(req,somefunc)

it will fail. This is because req.register_cleanup() is implemented in C code
and explicitly checks to see if the "req" argument matches the the type of
the C implemented <requestobject>. The check will fail and an exception
is raised.

Overall, the way of doing it is probably going to have to be tailored to the
individual circumstances it is being used for. Ie., whether access to the
request object and forms is needed or not etc. Not knowing the exact
circumstances the original poster wanted to use it in, I am sure we could
come up with all sorts of variations and still not come up with one which
would be the best for that case. At least the discussion generated may be
useful to someone. :-)

Graham

> def index(req):
>     fake_req = FakeRequest(req)
>     # do the PSP trick using fake_req and additional variables
>     req.write(fake_req.buffer.get_value())
> 
> Regards,
> Nicolas
> 
> 2005/6/1, Graham Dumpleton <grahamd at dscpl.com.au>:
> > Only just got the chance to revisit this. Here is an alternate bit of
> > code which avoids having to munge PSP code. This only works for
> > where PSP code is stored in a file. It bypasses any caching mechanism
> > present in the PSP class, but you could add your own if need be. It
> > also replaces the request object entirely with a dummy object purely
> > for the purposes of collecting output.
> > 
> > from mod_python import apache
> > from mod_python import _psp
> > 
> > import os
> > import StringIO
> > 
> > def _psp_to_str(filename,vars={}):
> >   directory,file = os.path.split(filename)
> > 
> >   code = _psp.parse(file,directory+'/')
> > 
> >   class _MPStringIO(StringIO.StringIO):
> >     def write(self,string,flush=1):
> >       StringIO.StringIO.write(self,string)
> > 
> >   vars["req"] = _MPStringIO()
> > 
> >   exec code in vars
> > 
> >   return vars["req"].getvalue()
> > 
> > def handler(req):
> >   directory = os.path.dirname(__file__)
> >   filename = os.path.join(directory,"_sample.psp")
> >   content = _psp_to_str(filename)
> > 
> >   req.content_type = "text/html"
> >   req.send_http_header()
> >   req.write(content)
> > 
> >   return apache.OK
> > 
> > 
> > Graham Dumpleton wrote ..
> > >
> > > On 31/05/2005, at 2:19 AM, Joshua Ginsberg wrote:
> > >
> > > > Hello --
> > > >
> > > > I'm trying to do something a little unorthodox... I didn't know if
> > > > anybody had tried it before... Within a PSP page, I want to run
> > > > another PSP page and capture the output to a string.
> > > >
> > > > To do a little testing, I wrote the following PSP page:
> > > >
> > > > <%
> > > >
> > > > def sAppend(self, thestring):
> > > >     if '__s__' not in dir(self):
> > > >         self.__s__ = ''
> > > >     self.__s__ += str(thestring)
> > > >
> > > > oldReqWrite = req.write
> > > >
> > > > req.write = sAppend
> > >
> > > This is a bit confusing. Your method accepts a "self" parameter as
> if
> > > it is a
> > > method of a class when it isn't. Just because you replace an existing
> > > instance
> > > method will not turn your one into one.
> > >
> > > The result is that the string to be output is being passed as self
> and
> > > string
> > > doesn't have a "__s__" member.
> > >
> > > There are other problems with using "__s__" as well but rather than
> go
> > > into it,
> > > you should just look at:
> > >
> > >    http://www.modpython.org/pipermail/mod_python/2005-May/018134.html
> > >
> > > That code could probably still be cleaned up a bit perhaps as certain
> > > things
> > > don't need to be done, also not sure why other things are being done
> > > either.
> > > If I get a chance later I'll see if I can come up with a more
> > > streamlined
> > > version.
> > >
> > > BTW, did you want to actually reference the original "req" object for
> > > any
> > > data. The example referenced obviously doesn't allow this as it
> > > completely
> > > replaces the "req" object with a StringIO object.
> > >
> > > Graham
> > >
> > > _______________________________________________
> > > Mod_python mailing list
> > > Mod_python at modpython.org
> > > http://mailman.modpython.org/mailman/listinfo/mod_python
> > _______________________________________________
> > Mod_python mailing list
> > Mod_python at modpython.org
> > http://mailman.modpython.org/mailman/listinfo/mod_python
> >


More information about the Mod_python mailing list