Differences between revisions 1 and 2
Revision 1 as of 2005-06-15 11:22:22
Size: 6689
Editor: ZoomQuiet
Comment:
Revision 2 as of 2009-12-25 07:14:04
Size: 6695
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 7: Line 7:
Aaron Brady [http://insom.me.uk/blog/Tech/Python/templating.writeback described using Cheetah under Quixote]. Aaron Brady [[http://insom.me.uk/blog/Tech/Python/templating.writeback|described using Cheetah under Quixote]].
Line 24: Line 24:
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). 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).
Line 179: Line 179:
Edd 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. Edd 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.

Problem

You wish to use the Cheetah templating library with Quixote.

Solution

Aaron Brady 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 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.


CategoryCookbook

QuixoteCookbook/TemplatingWithCheetah (last edited 2009-12-25 07:14:04 by localhost)