Problem
You wish to use the Cheetah templating library with Quixote.
Solution
Aaron Brady [http://insom.me.uk/blog/Tech/Python/templating.writeback described using Cheetah under Quixote].
Discussion
Aaron's approach simply creates a specific template in a Quixote handler. A more complicated alternative approach would be the StaticFile approach described on the TemplatingWithZpt page.
A more complicated alternative approach ;-)
Here's a little module that implements a StaticDirectory-like class that is Cheetah-aware.
Simple intro: if you have a Cheetah Directory handling requests at /foo/, then a call to /foo/bar will look for bar.tmpl (a Cheetah template) or bar.py (a Python script) and use that element to process the request. See the source (below) for more information.
Warning: it's slow. Templates are recompiled on every request. Perhaps someone could fix that if this actually gets used. Precompliation would be useful if you make use of the use_cache parameter when creating your Cheetah Directory.
There's a [http://fawcett.medialab.uwindsor.ca/quixote/qcheetah.tgz sample application] that uses this module. To run it out of the box, you'll need Quixote, Cheetah and Twisted (but you can always rewrite the server script, and use a different Web server).
"""
Tools to make Cheetah play nicely with Quixote.
Author: Graham Fawcett,
"""
from quixote.util import StaticDirectory
from Cheetah.Template import Template
import os
from StringIO import StringIO
class BaseHandler:
def make_environ(self, request):
environ = {
'cgi': request.environ,
'form': request.form,
'request': request,
'context': getattr(request, 'context', None), # see qxmods module
'template': self.filename,
'thisdir': request._thisdir + '/', # see CheetahDirectory
'rootdir': request._rootdir + '/', # see CheetahDirectory
}
return environ
class CheetahTemplate(BaseHandler):
"""
Represents a Cheetah template (a file ending with .tmpl) in your Web app.
Todo: probably should pre-compile the Template;
without precompilation, request handling is slow...
"""
def __init__(self, fn):
self.filename = fn
def __call__(self, request):
request.response.set_header('Cache-control', 'no-cache')
environ = self.make_environ(request)
x = str(Template(file=self.filename, searchList=[environ]))
return x
class PythonScript(BaseHandler):
"""
Represents a Python script (a file ending with .py) in your Web app.
Python scripts must define a function, 'def respond(request)' which
will process a Quixote request. They may return any valid Quixote
response type. Alternately, you can 'return printed', which will
return any text which the script printed to stdout.
"""
def __init__(self, fn):
self.filename = fn
codeobj = compile(
file(self.filename).read().strip() + '\n\n',
os.path.basename(self.filename),
'exec')
self.namespace = {}
exec(codeobj, self.namespace)
def __call__(self, request):
request.response.set_header('Cache-control', 'no-cache')
environ = self.make_environ(request)
printed = StringIO()
environ['printed'] = printed
self.namespace.update(environ)
try:
sys.stdout = printed
x = eval('respond(request)', self.namespace)
if x == printed:
x = printed.getvalue()
finally:
sys.stdout = sys.__stdout__
return x
class CheetahDirectory(StaticDirectory):
"""
Like StaticDirectory, wrap a filesystem directory containing static files
as a Quixote namespace. But also allow special handling for .tmpl and .py files.
See StaticDirectory.__init__ for signature of the constructor.
"""
def _q_index(self, request):
"""
Return a response from index.tmpl or index.py, if exists;
else return a directory listing if allowed.
"""
item = self._q_lookup(request, 'index')
if item:
return item(request)
else:
return StaticDirectory._q_index(self, request)
def _q_lookup(self, request, name):
"""
Get a file from the filesystem directory and return the StaticFile
or StaticDirectory wrapper of it; use caching if that is in use.
"""
# set _thisdir and _rootdir attributes on the request.
# _rootdir is set once, at the first occurrence of a CheetahDirectory.
if not hasattr(request, '_rootdir'):
request._rootdir = self.path
request._thisdir = self.path
if name in ('.', '..'):
raise errors.TraversalError(private_msg="Attempt to use '.', '..'")
if self.cache.has_key(name):
# Get item from cache
item = self.cache[name]
else:
# check if there's a dot in the name.
# if not, then /foo might refer to /foo.tmpl or /foo.py.
# Get item from filesystem; cache it if caching is in use.
item_filepath = os.path.join(self.path, name)
item = None
if os.path.isdir(item_filepath):
item = self.__class__(item_filepath, self.use_cache,
self.list_directory,
self.follow_symlinks, self.cache_time,
self.file_class)
elif os.path.isfile(item_filepath):
item = self.file_class(item_filepath, self.follow_symlinks,
cache_time=self.cache_time)
elif not '.' in name:
tmpl_name = item_filepath + '.tmpl'
py_name = item_filepath + '.py'
if os.path.isfile(tmpl_name):
item = CheetahTemplate(tmpl_name)
if os.path.isfile(py_name):
item = PythonScript(py_name)
if not item:
raise errors.TraversalError
if self.use_cache:
self.cache[name] = item
return itemEdd Dumbill [http://usefulinc.com/edd/blog/contents/2005/03/06-queetah/read posted a blog entry] that includes the above script updated to cache compiled Cheetah templates.
