See also: * ReviewBoardFeedback_ Cheetah: The Python-Powered Template Engine =========================================== *By Tavis Rudd, Mike Orr and Ian Bicking* Affiliations ------------ Tavis Rudd is a freelance consultant. Mike Orr does web application development at a publishing company. Ian Bicking is a freelance web developer. Abstract -------- Cheetah is a Python-powered template engine and code generator. It has many potential uses -- generating SQL, SGML, LaTeX_, PostScript_, form emails, etc. -- but its principle use is for web developers as an alternative to ASP, JSP, PHP and PSP. Cheetah integrates tightly with http://webware.sourceforge.netWebware for Python, but is also well suited for use as a standalone tool. The current version is available at http://www.cheetahtemplate.org/, along with a Users' Guide. Cheetah is distributed under a BSD-like license. This paper introduces Cheetah and examines its influences, design principles, and implementation. It also highlights the unique features that distinguish it from other template engines. Keywords -------- templates, code generators, web development, Webware for Python, servlets, parsers Introduction and Design Influences ---------------------------------- Cheetah is a template engine and code generator that grew out of the 'Webware for Python' project [1]. In April 2001, Webware developers could write servlets using only pure Python code or Python Server Pages (PSP) [2]. Neither facilitated the rapid creation of the large HTML templates for dynamic web applications. Vanilla Python is not tailored for extended string interpolation, nor should it be. PSP is well suited for throwing together a quick page or two, but tends to degrade into a tangled mess of spaghetti code when used in larger projects. It's also more verbose than necessary and is hard to explain to non-programmers. Several Webware developers decided to address this situation and started a discussion about porting or developing a template system that made it easy to access any Python data structure and manage control flow from within text documents. As we were fleeing spaghetti code, we felt that being able to separate the interface from the application code was essential. It should work well with any text-based output, not just HTML. Builtin output caching would be nice. In the name of maintainability, we wanted to be able to inherit and extend one template from another. Most importantly, the basic syntax and concepts needed to be simple and consistent enough for non-programmers to understand, while being flexible enough to give programmers the full expressive power of Python. Our wishlist was long! Over the following months, we closely examined many template systems with radically different approaches: Zope's DTML and Zope Page Templates [3], Alex Martelli's Yet Another Python Templating Utility (YAPTU) [4], Ka-ping Yee's string interpolation module (itpl) [5], Quixote's Python Template Language [6], Velocity [7] and WebMacro_ [8] from Java, Smarty [9] and PHPLib's Template class [10] from PHP, and the Template Toolkit from Perl [11]. Much later, we also looked at Skunkweb's STML [12]. We were tempted by Zope Page Templates (ZPT) because it seemed to work well for teams that had designers working with WYSIWYG tools. However, we decided against a ZPT-like design for several reasons. First, we wanted a generalized system that worked with any form of text output, not just HTML. A recent discussion on Advogato [13] highlighted our other concerns: [ZPT] seems like a good idea, but it has a number of serious problems. The biggest problem is that it assumes that the static structure of a web page is in any way similar to how that web page is going to look when displayed dynamically. However, most large web sites use the notion of "components" - that is, re-usable fragments of dynamic HTML which are assembled to form a complete page. ... What I have experienced is that HTML pages for industrial-strength web sites like Amazon's is [sic] made up of a myriad of dynamically generated elements - there is very little static structure to the page at all. Thus, it is only useful to preview the dynamic elements. Previewing the static structure of the page is not in the least bit helpful, and merely serves to make the artists' job harder by distracting them with "dummy" content that makes the template more verbose. ... The WYSIWYG paradigm I believe involves two basic assumptions: * That the developer of a piece of content is able to see, in real- time, the visual effects of a particular design modification. That the operations on the document are all fundamentally visual operations, that is, operations on appearance, layout and style. * In other words, I think that in order to be truly WYSIWYG, the content developers need not concern themselves with the abstractions that lie behind the visual presentation. They operate directly on the visual presentation and never need to penetrate beneath it. However, in a highly dynamic system, it really isn't possible to visually present any more than a snapshot of the dynamic system at any given moment. In a highly dynamic system, you really do want to expose to the designer the abstract mechanisms behind the facade - because those abstract elements are what defines the dynamics. It means that the designer has to think abstractly, and must operate on that level instead of the level of superficial appearances. ---- Talin_ (quoted with permission) .. _Talin: http://www.advogato.org/person/Talin/ ---------------------------------------- Code kept in HTML attributes is largely inaccessible to the user -- they can't see it, they won't know if they've deleted it, and they'll have a really hard time putting it in. [The designers] are capable of doing some programming -- the most simple and most common case is simply indicating where certain values should go (like username, status message, etc). In fact, one of ZPT's big detractions, in my mind, is just how difficult it is just to do that simple insertion (username, if I remember correctly). One of the great things about WYSIWYG editors is that they are entirely concrete. Now it's not possible to make a dynamic page entirely concrete, but it is nice to at least make the logic visible. And, of course, ZPT is connected to HTML and XML. But just about every significant application will also have to produce CSS, Javascript, and email messages. Maybe this is part of the transition you've already made from XML to HTML, and eventually to plain text. It's a significant problem, but a solvable one. In the same way, by putting in a non-HTML-attribute syntax, you can potentially make ZPT more concrete. I don't want to seem too negative -- there are some really good parts to ZPT, particularly when changing IMG SRC value, which will piss off the WYSIWYG editor badly in most other systems. ---- `Ian Bicking`_ Furthermore, ZPT assumes a clear division of roles between the programmer and the designer. We preferred a system with a generalized core that could adapt to uses and work flow patterns that weren't anticipated a priori. Our conclusion was that if we ever needed the WYSIWYG abilities of ZPT, we'd build an extra layer to provide it. None of the existing systems was a perfect match, so we began work on several competing modules that blended what we considered the best features in the existing systems. Cheetah's predecessor, ``TemplateServer_``, was one of these modules. It began as a simple regex-based tool, vaguely resembling YAPTU. As we examined the syntax and design philosophies used in other systems, and as its author slowly learned how to build a parser, TemplateServer_ evolved and gradually gained general support among the Webware developers. In June 2001, we retired the name ``TemplateServer_`` in favor of ``Cheetah``. Early on, we abandoned the pointy-bracket syntax of PSP and other many other systems because the tags are invisible in browsers when something goes wrong, are too verbose, and in general work poorly with GUI tools. Instead, we adopted the basic syntax of Velocity: ``$``'s for interpolation placeholders and ``#``'s for directives. Velocity also strongly influenced our perspective on syntax and complexity. Initially, several of us argued that Cheetah's syntax should be purposefully restricted, in order to prevent developers from doing complex processing in Cheetah. We'd seen the mess that DTML had become, so our maxim was "Cheetah for the simple tasks, Python for the complex". It soon became clear that this stance was unnecessary and would artificially limit Cheetah's usefulness. DTML had failed because of its confusing, inconsistent syntax and the many hoops one had to jump through to access plain Python, not because it had given template writers powerful constructs to work with. Velocity, on the other hand, was succeeding because it had a coherent syntax that was expressive enough to handle most aspects of an application's *interface* without requiring constant switches back to Java. Cheetah users were complaining about being forced back into Python to handle complex tasks that were still related to the *interface*, so we decided to make most of Python's language constructs directly accessible from Cheetah. Where Python has ``for ...``, Cheetah has ``#for ...``, and so on. As a result, Cheetah's syntax became larger, but not necessarily more complex. The original core is still easy for non-programmers to learn and use. And, as most Cheetah language constructs are exact mirrors of Python contructs, prefaced with a ``#`` or ``$``, someone with Python experience won't have much re-learning to do. Cheetah's object-orientation and error handling are semantically identical to Python's. See the *Syntax Overview* section below for more information. Cheetah's original implementation translated templates into function definitions, evaluated them and then dynamically bound the resulting function as a method of a ``Template`` object, much like the cached subroutines used by Perl's Template Toolkit. This approach was flawed. Subtle ``tricks`` were needed for the inheritance features to work and we were constantly running into conceptual brick walls and ambiguities. Templates could be extended and reused, but via our own black magic system rather than via Python's standard inheritance structures. It also led to high initialization costs and painful debugging. Webware's implementation of PSP gave us the idea to compile templates into pure Python classes / modules. Most of Cheetah's early problems were resolved by this decision and most of its current strengths and unique features are a direct result of it. Syntax Overview --------------- Cheetah has two types of tags: placeholders and directives. Placeholder tags begin with a dollar sign (``$varName``) and are similar to data fields in a form letter or the %(key)s fields Python's % operator uses. An alternate syntax (``${varName}``) may be used to prevent ambiguity about where the placeholder ends. Directive tags begin with a hash character (#) and are used for comments, loops, conditional blocks, includes, and all other features. Inside directive tags, Cheetah uses a Python-like syntax and understands any valid Python expression. The main differences from pure Python syntax are 1) all variable names are prefaced with a dollar sign ($), 2) colons (:) are not used to mark the beginning of a code block, and 3) indentation is not significant. Instead of using indentation, Cheetah uses ``#end [if | for | try | etc.]``. Most of the directive tags are direct mirrors of Python statements. Cheetah also supports two PSP style tags as escapes to pure Python code. These are not part of Cheetah's core syntax, but are included to facilitate migration from PSP-style markup languages to Cheetah. These tags may not be used inside of other Cheetah tags, and vice versa. * evalute expression and print the output: ``<%=`` ...``%>`` * execute code and discard output: ``<%`` ...``%>`` Cheetah's Language Constructs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following is a basic categorization of Cheetah's language constructs. A detailed explanation is provided in the Users' Guide. * Comments and documentation strings: * ``## single line`` * ``#* multi line *#`` * Generation, caching and filtering of output: * plain text * output from simple expressions: ``$placeholders`` * output from more complex expressions: ``#echo`` * gobble the end of line: ``#slurp`` * parsed template file includes: ``#include`` * raw file includes: ``#include raw`` * verbatim output of Cheetah code: ``#raw`` ...``#end raw`` * cached placeholders: ``$*var``, ``$**var`` * cached regions: ``#cache`` ...``#cache`` * set the output filter: ``#filter`` ... * Function/method calling without outputting the return value: ``#silent`` * Importing other modules and from other modules: ``#import``, ``#from`` * Inheritance: * set the base classes to inherit from: ``#extends`` * set the name of the main method to implement: ``#implements`` * Compile-time declaration: * define class attributes: ``#attr`` ... * define class methods: ``#def`` ...``#end def`` * ``#block`` provides a simplified interface to ``#def`` * define class 'settings': ``#settings`` ...``#end settings`` using the builtin SettingsManager API * Run-time assignment: * local vars: ``#set`` ... * global vars: ``#set global`` ... * Flow control: * ``#if`` ...``#else`` ...``#else if`` (aka ``#elif``) ...``#end if`` * ``#for`` ...``#end for`` * ``#while`` ...``#end while`` * ``#break`` * ``#continue`` * ``#pass`` * ``#stop`` * error/exception handling: * ``#assert`` * ``#raise`` * ``#try`` ...``#except`` ...``#else`` ... ``#end try`` and ``#finally`` * ``#errorCatcher``: a debugging tool that sets a default exception catcher/handler for exceptions raised by $placeholder calls. * Instructions to the parser/compiler: * ``#breakpoint`` * ``#compiler-settings`` ...``#end compiler-settings`` How Cheetah Works ~~~~~~~~~~~~~~~~~ The heart of Cheetah is the ``Template`` class in the ``Cheetah.Template`` module. It serves two purposes. First, its constructor method accepts a source string or file (filename or file object) and compiles the source into a Python class. Second, it is used as the base class for the generated classes. ``Template`` subclasses Webware's ``HTTPServlet`` class when it is available. Thus, the generated classes can be used as Webware servlets or as standalone utilities. Generated template classes can either be used immediately or written to a Python module file for future use. In the former case, the methods and attributes of the generated class are dynamically added to the instance of ``Template`` that did the compiling. They are available as soon as compilation is complete. In the latter case, Cheetah will wrap the generated class definition in some boilerplate code to produce a complete module definition. The file ``Example.tmpl`` contains the following source code:: #attr adj = "trivial" This is a $adj example This can processed using the python interactive interpreter:: >>> from Cheetah.Template import Template >>> template = Template(file='Example.tmpl') >>> print template This is a trivial example >>> print template # and again This is a trivial example >>> >>> print template.generatedClassCode_() # ... the source code of the generated Python class (see the Appendix for examples) >>> print template.generatedModuleCode_() # ... the source code of the generated Python module Cheetah source files (``.tmpl``) can also be processed in a variety of ways using the command line:: tavis@lucy: ~ > cheetah-compile -h Cheetah 0.9.9b1 command-line compiler by Tavis Rudd and Ian Bicking Compiles Cheetah files (.tmpl) into Webware servlet modules (.py) Usage: cheetah-compile [OPTIONS] FILES/DIRECTORIES -R Recurse subdirectories -p Print generated Python code to stdout -w Write output of template to *.html -v Be verbose tavis@lucy: ~ > cheetah-compile -p Example.tmpl | python This is a trivial example tavis@lucy: ~ > ls Example.* example.tmpl tavis@lucy: ~ > cheetah-compile example.tmpl tavis@lucy: ~ > ls Example.* Example.tmpl Example.py tavis@lucy: ~ > python Example.py This is a trivial example The Python modules that are generated from templates come with a command-line interface themselves:: tavis@lucy: ~ > export adj=commandline tavis@lucy: ~ > python Example.py --env This is a commandline example All of Cheetah's command line interfaces accept input via pipes and can be chained together with other tools, as demonstrated by this contrived example:: echo "My computer's name is $HOST" | cheetah-compile - | python - --env In highly simplified pseudo-code, ``Example.py`` works something like this:: from Cheetah.Template import Template class Example(Template): adj = "trivial" def respond(self, trans=None, # trans is a Webware 'transaction' object dummyTrans=False): """ This is the main method generated by Cheetah """ if not trans: # i.e. if run without Webware trans = DummyTransaction_() dummyTrans = True write = trans.response().write # used to stream output ######################################## ## START - generated method body write('This is a ') write(str(self.adj)) # generated from '$adj' at line, col (2, 11). write(' example') ######################################## ## END - generated method body if dummyTrans: return trans.response().getvalue() else: return "" ################################################## ## GENERATED ATTRIBUTES adj = "trivial" __str__ = respond ################################################## ## if run from command line: if __name__ == '__main__': Example().runAsrogram() Webware servlets use the ``respond()`` method to respond to incoming requests. It is also the core method of a Cheetah generated class. If ``Example.py``, or any other Cheetah generated module, is placed in a Webware servlet directory it will be loaded like any other Webware servlet module. For convenience, Cheetah makes the ``__str__`` method an alias to ``respond()``. Thus, ``print myTemplateObj_`` or ``print Template(sourceString)`` will print the output of the template. Getting Data Into Cheetah Templates ----------------------------------- There are numerous options for getting data into Cheetah templates so formatted output can be generated from it. These options are identical to those available in normal Python: importing from modules, querying databases, using mixin classes, etc. The Cheetah Users' Guide contains examples. What Makes Cheetah Unique? -------------------------- Object-Oriented Documents ~~~~~~~~~~~~~~~~~~~~~~~~~ As described above, Cheetah templates are class definitions. Whereas other template systems use the concept of ``blocks`` to define regions of a template that can be overridden and customized, Cheetah uses methods, attributes and inheritance: #attr anAttrib = "An attribute of the template class" #def myMethod1 the first method #end def #def myMethod2 the second method #end def Here's $anAttrib Here's $myMethod1 Here's $myMethod2 Methods can have arguments:: #def myMethod($arg1='foo', $arg2='bar') This is arg1: $arg1 This is arg2: $arg2 #end def $myMethod(1,2) Cheetah also provides the ``#block`` directive as a simple wrapper around the ``#def`` directive. It allows a method to be defined and used in place. :: #block myMethod This is a method defined and used in place. #end block is semantically identical to:: #def myMethod This is a method defined and used in place. #end def $myMethod NameMapper Variable Access ~~~~~~~~~~~~~~~~~~~~~~~~~~ One of our core aims with Cheetah was to make it easy for non-programmers to use. To achieve this aim, we created a simplified syntax for mapping variable names in Cheetah to values in Python. It's known as the **NameMapper_ syntax** and makes it possible for non-programmers to use Cheetah without knowing (a) what the difference is between an object and a dictionary, (b) what functions and methods are, and (c) what 'self' is. NameMapper_ syntax is used for all variables in Cheetah placeholders and directives. Consider this scenario: You've been hired as a consultant to design and implement a customer information system. You create a class that has a 'customers' method that returns a dictionary of all the customer objects. Each customer object has an 'address' method that returns the a dictionary with information about the customer's address. The designers working for your client want to use information from your system on the client's website **and** they want to maintain the display code themselves. Using PSP, the display code for the website might look something like the following:: <%= self.customer()[ID].address()['city'] %> (42 chars) Without understanding objects, methods, and dictionaries the designers must rely on rote memory to know where to put the parentheses, brackets and quotation marks. NameMapper syntax insulates them from this complexity and is forgiving with the small syntactical subtleties that PSP would gag on. All of the following variations are valid with NameMapper:: $self.customers()[$ID].address()['city'] (39 chars) --OR-- $customers()[$ID].address()['city'] --OR-- $customers()[$ID].address().city --OR-- $customers()[$ID].address.city --OR-- $customers()[$ID].address.city --OR-- $customers[$ID].address.city (27 chars) NameMapper may initially scare those who have experienced the quirks of Zope's ``acquisition`` and ``DTML``. If desired, it can be turned off using the ``'useNameMapper'`` setting. However, we have yet to find a case where this is needed, as plain Python syntax is compatible with NameMapper. Cheetah comes with two implementations of the module that implements NameMapper syntax: one in C and the other in Python. The C version is up to 6 times faster. It's still slightly slower than standard Python syntax, but the speed difference is neglible in real world scenarios. Cheetah uses the optimized C version if it has been compiled, and automatically falls back to the Python version if not. Completely Configurable Syntax ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We made the potentially controversial decision to allow users to use any delimeters in place of the standard ``$`` and ``#``. The delimeters for comments are also configurable. This is a safety valve for situations we didn't anticipate, where the standard delimiters are just too impractical. We wanted something that is flexible enough to handle unanticipated situations in the future. In particular, configurable delimiters allow for the dynamic creation of tutorials, program listings, or even source code listings of other languages that use $ for special purposes. For example, preprocessing of LaTeX source files is one area that we have a strong interest in. People often raise concerns that this might lead to conflicts between templates configured to use different delimiters. This is not a problem, as each Cheetah template is individually compiled to pure Python code. Thus, such changes are localized to a single file. A template with one set of delimiters can safely subclass or even include a template that uses another set. A very unique feature of Cheetah is that these delimiters can be changed for an entire template file, or just a particular portion of a template. Powerful Output Caching Framework ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cheetah can cache the output from individual ``$placeholder`` tags or from entire regions of Cheetah source code. Caches can static or linked to a refresh-timer. The syntax for using the caching framework is incredibly simple. To cache a placeholder statically, use ``$*varName``. To cache a placeholder with a refresh-timer, use ``$*5m*varName`` (or ``$*5m*{varName}``). Times may be specified in fractional numbers and with various interval suffixes ('s' for seconds, 'h' for hours, 'd' for days, 'w' for weeks). Thus, ``$*0.5d*varName`` means cache for half a day. To cache a region, use the ``#cache`` directive. Cached regions can be given an ``id``, which can be used to programmatically refresh a region. Cached regions can also be invalidated according to user-specified test conditions. :: #cache id='sidebar', test=$isDBUpdated ## do something #end cache #cache id='sidebar', test=($isDBUpdated or $someOtherCondition_) ## do something #end cache The ``#cache`` directive is currently undergoing some extensions. Proposed extensions would allow caching according to a query-string parameter (``varyByParam='ID'``), the browser type (``varyByBrowser``). Tight Integration with Webware, While Being Separable ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cheetah integrates tightly with the Webware servlet model, but can also be used as a standalone tool or as part of other systems. For, example every template processed with Cheetah has a builtin command line interface for use with shell scripts. Current Status -------------- As of this writing (December 2001), Cheetah is beta software, although it is already being used in several production applications. The existing syntax and semantics are stable, but there are some additions to come. Version 1.0 will be released "when it's ready". We're hoping that will be early 2002, but we would rather "do it right" than "do it hastily". Our goals are robustness, scalability, and applicability to a wide variety of situations. Future Directions ----------------- WYSIWYG Editor Integration ~~~~~~~~~~~~~~~~~~~~~~~~~~ While effort has been made to make Cheetah friendly with WYSIWYG systems, the test of actual use has yet to be made. In particular, WYSIWYG editors generally demand to make valid HTML documents, and that can conflict with Cheetah as it conflicts with many template systems. The most difficult problem is a case like:: #for $row in $values #end for
$row
Anything between and is generally invalid, and WYSIWYG editors will rearrange these pieces. More Documentation ~~~~~~~~~~~~~~~~~~ Cheetah currently has a 60 page Users' Guide (see the Cheetah home page) and a collaborative Wiki. A Beginners' Guide, and a Developers' Guide are planned. The wiki site is home to an Examples Cookbook describing various usage strategies Code and Template Libraries ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cheetah comes "batteries included" with a library of templates, functions and other objects you can use in your own programs. Some of these were contributed by users. We are very interested in building up this library. Conclusion ---------- Like its namesake, Cheetah is fast, flexible and powerful. If you're a Python programmer looking for a templating system that is Pythonic, consider Cheetah. If you like Webware because of its modular nature, you'll like Cheetah. If you require a division of labor between the application programmers and designers (who may not know how to program), Cheetah is a good match. Acknowledgments --------------- Chuck Esterbrook provided the original inspiration for Cheetah and has been an active contributor throughout the project. We'd also like to thank the following people for contributing valuable advice, code and encouragement: Geoff Talvola, Jeff Johnson, Graham Dumpleton, Clark C. Evans, Craig Kattner, Franz Geiger, Tom Schwaller, Rober Kuzelj, Jay Love, Terrel Shumway, Sasa Zivkov, Arkaitz Bitorika, Jeremiah Bellomy, Baruch Even, Paul Boddie, Stephan Diehl, Chui Tey, and Geir Magnusson. The Velocity and WebMacro_ projects provided inspiration and design ideas. Cheetah has benefitted from the creativity and energy of their developers. Appendix -------- Detailed Example ~~~~~~~~~~~~~~~~ The following template is part of Cheetah's standard library:: #*doc-module: A Skeleton HTML page template, that provides basic structure and utility methods. *# ################################################################################ #from Cheetah.Templates._SkeletonPage_ import _SkeletonPage_ #extends _SkeletonPage_ #implements respond ################################################################################ #settings python title = 'Skeleton Page Template' siteDomainName_ = 'www.CheetahTemplate_.org' siteCopyrightName_ = 'Tavis Rudd' siteCredits = 'Designed & Implemented by Tavis Rudd' metaTags = {'HTTP_EQUIV':{'keywords':'Cheetah,'}, 'NAME':{'generator':'Cheetah: The Python-Powered Template Engine'} } bodyTagAttribs_ = {'text':'black'} #end settings ################################################################################ #cache id='header' $docType ################################################################################ #block writeHeadTag_ $title $metaTags $stylesheetTags $javascriptTags #end block writeHeadTag_ #end cache $bodyTag #block writeBody This skeleton page has no flesh. Its body needs to be implemented. #end block writeBody This is its corresponding Python module:: #!/usr/bin/env python """A Skeleton HTML page template, that provides basic structure and utility methods. Autogenerated by CHEETAH: The Python-Powered Template Engine CHEETAH VERSION: 0.9.9a7 Generation time: Mon Dec 3 23:42:34 2001 Source file: SkeletonPage_.tmpl Source file last modified: Wed Oct 10 17:17:10 2001 """ __CHEETAH_genTime__ = 'Mon Dec 3 23:42:34 2001' __CHEETAH_src__ = 'SkeletonPage_.tmpl' __CHEETAH_version__ = '0.9.9a7' ################################################## ## DEPENDENCIES import sys import os import os.path from os.path import getmtime, exists import time import types from Cheetah.Template import Template from Cheetah.DummyTransaction_ import DummyTransaction_ from Cheetah.NameMapper import NotFound, valueForName, valueFromSearchList import Cheetah.Filters as Filters import Cheetah.ErrorCatchers as ErrorCatchers from Cheetah.Templates._SkeletonPage import _SkeletonPage ################################################## ## MODULE CONSTANTS True = (1==1) False = (1==0) ################################################## ## CLASSES class SkeletonPage(_SkeletonPage): """ Autogenerated by CHEETAH: The Python-Powered Template Engine """ ################################################## ## GENERATED METHODS def __init__(self, *args, **KWs): """ """ _SkeletonPage_.__init__(self, *args, **KWs) self._filePath = 'SkeletonPage_.tmpl' self._fileMtime = 1002759430 self.updateSettingsFromPySrcStr_('''title = 'Skeleton Page Template' siteDomainName_ = 'www.CheetahTemplate_.org' siteCopyrightName_ = 'Tavis Rudd' siteCredits = 'Designed & Implemented by Tavis Rudd' metaTags = {'HTTP_EQUIV':{'keywords':'Cheetah,'}, 'NAME':{'generator':'Cheetah: The Python-Powered Template Engine'} } bodyTagAttribs_ = {'text':'black'} ''') def writeHeadTag_(self, trans=None, dummyTrans=False, VFS=valueFromSearchList_, VFN=valueForName_, getmtime=getmtime, currentTime=time.time): """ Generated from #block writeHeadTag_ at line, col (34, 1). """ if not trans: trans = DummyTransaction_() dummyTrans = True write = trans.response().write SL = self._searchList filter = self._currentFilter globalSetVars_ = self._globalSetVars_ ######################################## ## START - generated method body write('\n') write(filter(VFS(SL,"title",1))) # generated from '$title' at line, col (36, 8). write('\n') write(filter(VFS(SL,"metaTags",1))) # generated from '$metaTags' at line, col (37, 1). write(' \n') write(filter(VFS(SL,"stylesheetTags",1))) # generated from '$stylesheetTags' at line, col (38, 1). write(' \n') write(filter(VFS(SL,"javascriptTags",1))) # generated from '$javascriptTags' at line, col (39, 1). write('\n\n') ######################################## ## END - generated method body if dummyTrans: return trans.response().getvalue() else: return "" def writeBody(self, trans=None, dummyTrans=False, VFS=valueFromSearchList_, VFN=valueForName_, getmtime=getmtime, currentTime=time.time): """ Generated from #block writeBody at line, col (47, 1). """ if not trans: trans = DummyTransaction_() dummyTrans = True write = trans.response().write SL = self._searchList filter = self._currentFilter globalSetVars_ = self._globalSetVars_ ######################################## ## START - generated method body write('This skeleton page has no flesh. Its body needs to be implemented.\n') ######################################## ## END - generated method body if dummyTrans: return trans.response().getvalue() else: return "" def respond(self, trans=None, dummyTrans=False, VFS=valueFromSearchList_, VFN=valueForName_, getmtime=getmtime, currentTime=time.time): """ This is the main method generated by Cheetah """ if not trans: trans = DummyTransaction_() dummyTrans = True write = trans.response().write SL = self._searchList filter = self._currentFilter globalSetVars_ = self._globalSetVars_ ######################################## ## START - generated method body if exists(self._filePath) and getmtime(self._filePath) > self._fileMtime: self.compile(file=self._filePath) write(getattr(self, self._mainCheetahMethod_)(trans=trans)) if dummyTrans: return trans.response().getvalue() else: return "" write('\n') ## START CACHE REGION: at line, col (19, 1) in the source. RECACHE = True if not self._cacheData.has_key('63190619'): self._cacheIndex['header'] = '63190619' pass else: RECACHE = False if RECACHE: orig_trans = trans trans = cacheCollector = DummyTransaction_() write = cacheCollector.response().write write(filter(VFS(SL,"docType",1))) # generated from '$docType' at line, col (20, 1). write(''' ''') write(self.writeHeadTag_(trans=trans)) # generated from '#block writeHeadTag_' at line, col (34, 1). write('\n') trans = orig_trans write = trans.response().write self._cacheData['63190619'] = cacheCollector.response().getvalue() del cacheCollector write(self._cacheData['63190619']) ## END CACHE REGION write('\n') write(filter(VFS(SL,"bodyTag",1))) # generated from '$bodyTag' at line, col (45, 1). write('\n\n') write(self.writeBody(trans=trans)) # generated from '#block writeBody' at line, col (47, 1). write(''' ''') ######################################## ## END - generated method body if dummyTrans: return trans.response().getvalue() else: return "" ################################################## ## GENERATED ATTRIBUTES __str__ = respond _mainCheetahMethod__for_SkeletonPage_`` 'respond' # CHEETAH was developed by Tavis Rudd, Chuck Esterbrook, Ian Bicking and Mike Orr; # with code, advice and input from many other volunteers. # For more information visit http://www.CheetahTemplate_.org ################################################## ## if run from command line: if __name__ == '__main__': SkeletonPage_().runAsrogram() References ---------- [1] Webware is an application server written in Python. It provides a persistent servlet framework similar to J2EE. http://webware.sourceforge.net/ [2] Python Server Pages: http://webware.sourceforge.net/Webware/PSP/Docs/UsersGuide_.html [3] Zope's DTML and ZPT: http://zope.org/ [4] Alex Martelli's YAPTU (Yet Another Python Templating Utility): http://aspn.activestate.com/ASPN/Python/Cookbook/Recipe/52305 [5] Ka-ping Yee's itpl module: http://zope.org/ [6] CNRI's Quixote and Python Template Language: http://www.mems-exchange.org/software/quixote/ [7] Velocity: http://jakarta.apache.org/velocity [8] WebMacro: http://webmacro.org/ [9] Smarty: http://www.phpinsider.com/php/code/Smarty/ [10] PHPlib: http://phplib.sourceforge.net/ [11] Perl's Template Toolkit: http://search.cpan.org/doc/SAMTREGAR/HTML-Template-2.4/Template.pm [12] Skunkweb: http://skunkweb.sourceforge.net/ [13] Discussion about Zope Page Templates on Advogato: http://www.advogato.org/article/350.html --------------------------------------------------------------------------- Comments: --------- We should add a "what Python could do better" section, to suggest ways in which compiling to Python was less than optimal. It might reach an interesting audience. The one example I can think of is with error messages. It's hard to associate the original code with the compiled code during runtime errors. But there's other things too, I'm sure. -- IanBicking_ - 29 Nov 2001 ----- A great start, but perhaps the audience might be interested in a more detailed architecture description. i.e., how do all the bits fit together "behind-the-scenes." -- EdmundLian_ - 06 Dec 2001 ------------------------------------------------------------------- Our goal is to make Cheetah, if not part of Python's standard library, at least the package of choice for any non-trivial use of templates. Focus on Cheetah's applicability to a variety of situations, and how easily it flows with Python, then after that detail how it enhances Webware. To differentiate this paper from the User's Guide (UG), make it: 1. more technical. Let the UG explain how to do things. Here describe how Cheetah is structured. 2. focused on "why" throughout. Why did we need yet another template language? Why didn't we use one of the exisiting ones? Why did we design it differently? Describe the why of each evolutionary step. Describe what we tried and rejected and why. Again, the UG explains "how", so we don't have to. Focus the paper on a more human aspect: what our needs are, and how Cheetah fills them. That'll make ppl want to use Cheetah, if they find their needs are similar to ours. Responding to the Review Board's criticisms: * Explain why Cheetah is different than Python re: uniform dotted notation, autocalling certain callables but not others. #block confusing. How are similar affects achieved (or not achieved) in other template systems? * Cover technical details not in the UG. * Show the generated code. * More comparision with PSP and DTML. Section-by-section comments --------------------------- Introduction and history ------------------------ "Many were interested in exploring other approaches." Perfect place to explain why, and how Cheetah differs from Python, the other Python approaches, and non-Python approaches. Mention how Cheetah compiles into pure Python modules and classes, so Cheetah is really a kind of "Python preprocessor for text-intensive situations". Quick Example ------------- Change: substitutions = { ...} t = Template(tmpl, nameSpaces=[substitutions]) to: t = Template(tmpl) t.title = 'Contact List' t.clients = clients Then under the example, say "an alternate way to do the same thing:" substitutions = { ...} t2 = Template(tmpl, nameSpaces=[substitutions]) print t2 Design Principles ----------------- Good as is. Does Tavis have anything more to add now? Maybe say something about how Cheetah compiles into pure Python modules. (A retroactive goal!) How Cheetah works ----------------- Good section. It is used as the base class for the generated CLASS, not "classes". "Implement multiple methods": ppl aren't going to know what this means. Instead, say that the calling routine can determine the name of the main method (instead of ".respond"). Skip the details of how to do this. Expand on cheetah-compile: it has a lot more features and uses now. Borrow stuff from the How It Works chapter in the UG. This is a major feature no other template system has, especially for Python. Object-oriented documents ------------------------- Rewrite to show current usage of #block. This may be a good point to explain #block and #def (and #attr?). Also need to show #def with Cheetah syntax. Maybe it's better to insert a section before this explaining #def, then explain #block here and show how a #def can reimplement a #block (and every $placeholder with the same name, even if it occurs before the redefine). Language overview ----------------- Move the comparision with the %-operator somewhere below, when you talk about how Cheetah compares/contrasts with Python and other systems. Remove the colon and indentation bit, or subsume them under the #tag ... #end tag explanation. Language constructs ------------------- Update for current repitoire. Shorten this section if you can. Directive highlights -------------------- Maybe moving the Object-oriented section after this section will answer my previous concern about explaining #def and #block. NameMapper ---------- Expand this section, explain more of the "why's", more of the implementation structure. Mention that the NameMapper functions may be imported into other programs too. What makes Cheetah unique? -------------------------- I'm not sure if this section fits _here_, but the info belongs somewhere. Elaborate more on each point, explaining more "why's" and comparisions. Experiment with 'components' ---------------------------- I don't know what "component-based programming" means or how Cheetah experiments with it. Remove this section? Lessons learned --------------- Expand this section with more examples. Change it from a "we're teaching you" style to a "this is what we needed and what we tried and why it failed and what we're using instead" style. Let the advice to the reader speak for itself, rather than preaching it. Conclusion ---------- Add a paragraph tying in the themes of the paper more. -- MikeOrr_ - 08 Dec 2001