[Welcome] [TitleIndex] [WordIndex


You wish to use the Cheetah templating library with Quixote.


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.


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',
        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
            sys.stdout = printed
            x = eval('respond(request)', self.namespace)
            if x == printed:
                x = printed.getvalue()
            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)
            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]
            # 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.follow_symlinks, self.cache_time,
            elif os.path.isfile(item_filepath):
                item = self.file_class(item_filepath, self.follow_symlinks,
            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.


2010-09-22 22:14