Problem
You wish to use the Cheetah templating library with Quixote.
Solution
Aaron Brady described using Cheetah under Quixote.
Mike Orr in this Linux Gazette article includes a tip for using quixote.html.htmlescape with Cheetah.
Andrew Kuchling on Oct 10 2005 notes his usage of Cheetah with 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 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 item
Edd Dumbill posted a blog entry that includes the above script updated to cache compiled Cheetah templates.