Python templating languages

Author: Olivier Guilloux
Revision: 1.4
Date: 2004-06-12
Copyright: Olivier Guilloux

Table of contents


How to produce the HTML that makes the user interface of a web based, dynamic, application? This is one of the key questions that web developers need to answer.

And, in the Python world, there are many answers. Tons of template languages, XML transformation tools, and other solutions make up a laberynth where it is easy to get lost.

This article has been written in order to help developers to make a good choice, by providing an overview of the whole spectrum of tools available, classifying them by different criteria.

A more deeper comparison will be made of the most representative solutions, including benchmarks.


Chosing a template language in the Python world is quite difficult because of the enormous number of languages that are available.

In the first part of this article we will propose a way to sort these languages in different categories.

In the second part of this article We will analyse different criteria that could be used to make a good choice and we will illustrate these criteria with four template languages.

This article is the first step of the study, actually some criteria are not fully measured. The render speed is not measured yet for the four template languages used to illustrate the criteria. The next version of the article will fill this gap.

Let's start with the template languages categories.

Sort template languages

Python offers two ways to generate html pages : with or without a template. In this article we will focus on the first, it's to say template languages. Python template languages can be classified in three main categories:

The first category concerns template languages which add markups or escape code in the template. For example DTML adds new markups and PSP webware escapes code. The second category of Python template languages we have identified, there are the XML well formed or XHTML templates. We can divide the XML well formed templates into two sub-categories: the first one contents languages that allow embded code in the template (ZPT like), the second one is a set of template languages that do not allow embded code in the template (STL, PyMeld like).

With template

Add tags or comments to html

  • specific markup (DTML, HTMLTMPL like)
    • DTML
  • escaped code no new markup (Cheeta)
    • Cheetah
    • Preppy
    • Webware PSP

XML well formed (XHTML)

  • with embded code in template -> callback ZPT
    • ZPT
    • PXLT
  • without embded code in template-> nsFromPython: pipeline STL
    • STL
    • PyMeld (need to know the structure of the HTML, no hole in the template)

Without template

  • No tools (print "<html>%s</html>")
  • Tools
    • PTL
    • HTMLgen

To chose a template language, the developers can compare different criteria. Is the template language able to generate HTML / XHTML or XML? Is the template language able to read in a web designer tool? Does the language allow embded code? Is the upstream work important? Is it an efficient template language? Is it easy to maintain?

In this article we will deal with 5 main points : documentation, the power of the language, the learning curve, the rendering speed and one that is not easily measured or characterized, the language community. In fact, in oder to characterize a community, we need to know many information such as the number of persons it is made of, how they use the language and the number of persons that contribute to the development of the template language.

Template language documentation overview

In this section we will deal with the documentation.

The criteria used to measure the documentation:

  • content of the documentation
  • update of the documentation
  • activity of team rooms


The DTML Reference provides enough information to start to practice dtml within a short time. Moreover the DTML Reference explains how to use advanced DTML tags like dtml-tree, dtml-sendmail and dtml-mime, thanks to this documentation the developers may know some advanced dtml directives in a rather acceptable time.

The DTML template language is well documented, and it is easy to find documentation on the internet. The content of documentation gives a good vision of the capabilities of the language. Moreover the interactive documentation in the Zope Book allows people to edit comments, which add interesting information.

Cheetah template

The Cheetah user's guide contents many information from the Cheetah installation to the use of this template with Webware. The documentation discribes all the capabilities of the languages : import, inheritance, flow control, etc.

Moreover a developers'guide is available for the ones who want to learn more about Cheetah internal mecanisms.

The Cheetah template language is well documented. Personally I have learnt basic Cheetah directives within a few hours thanks to this documentation.


The ZPT/PT documentation contents all necessary information to practice ZPT. Like DTML documentation, the ZPT documentation allows people to edit comments.


No documentation at this time.

To conclude on this section we can synthetize the level of documentation in this table:

  Cheetah DTML ZPT STL
Documentation 2 2 2 0

0: No documentation, 3: Excellent


In this section we will try to measure and qualify the communities. We will try to rate the communities through the measure of the applications that use the template languages; for example the community of ZPT/PT corresponds with the Zope community. Moreover we can use the number of downloads done on the package (if it this data is available). But this way of measuring must be taken with the greatest precautions.

Developers and high level decision makers don't give the same importance to the role the community has. Developers may consider the community is important only if they need help or information. For high level decision makers the community is important only if it is a guarantee of perpetuating the template language.


The size of the DTML community can corresponds with the traditional Zope community size.

The quality of the community is difficult to measure.


The size of Cheetah community can corresponds with a part of the webware community size, and with a part of the cgi community size.

The quality of the community is difficult to measure.


The ZPT community size can corresponds with the modern Zope community size.

The quality of the community is difficult to measure.


The itools community and a focus on the ikaaro community.

The quality of the community is difficult to measure.

We can conclude that the STL community is the smallest of this four template languages, and that the ZPT community is certainly the most important one thanks to Zope.

This table gives an overview of the community charaterization:

  Cheetah DTML ZPT STL
Quantitative rate 2 2 3 0
Qualititive rate X X X X

0: No community or very small community, 3: Important community X: No data

Learning curve

In this section we will deal with the learning curve.

The criteria we choosed to measure it are:

  • The number of keywords
  • The easyness/the hardness to read the template code
  • The easyness/the hardness to write a template code


The dtml language is like a new mini-language, with its set of directives including loops, conditionals, exception treatments, and advanced treatments such as mail building, tree building. Thus the syntax of the dtml template language seems to be a bit long to learn. In fact the number of keyword is important:

dtml-if, dtml-elif, dtml-else, dtml-tag, dtml-var, dtml-call, dtml-unless,
dtml-in, dtml-with, dtml-let, dtml-raise, dtml-except, dtml-finally,
dtml-comment, dtml-return, dtml-tree, dtml-sendmail, etc. 

Moreover the dtml contents a set of specific variables and attributes like: expr, fmt (to format string), capitalize, upper, sort, sequence-item, nowrap, AND SO ON... - there are about 30 keywords (variables or attributes).

dtml sample:

<dtml-let ns="namespace()">
<dtml-in "ns.get('records')">
  <dtml-let record=sequence-item 
    record <i><dtml-var number></i> : 
             <b><dtml-var name></b><br/> 
      The car mark is : <b><dtml-var mark></b>
      The color is : <b><dtml-var color></b>
      <dtml-if technicals_data>
         The <a href="<dtml-var technicals_data>">technicals data</a> are
         available for the <b><dtml-var color></b>

The new tags added in the html by dtml are not easy to read for the developers who don't write the template. In addition the dtml syntax adds heaviness to the template. Consequently it is difficult to maintain and to update.

To complete on the DTML language we can say that practice dtml is not easy, that using dtml with the entire knowledge of the language needs rather a long training and that reading DTML template is difficult.

Cheetah Template

The Cheetah template language belongs to the "escape code syntax" template category. The dynamic code is prefixed by the markup:#. And $ is used to escape variables. The Cheetah template language uses a mini-language which integrates conditionals, loops and variable substitutions. As a result, learning Cheetah is equivalent to learning a mini-language.

The Cheetah template language contents an important set of keywords, for example:

## #* for comment, 

$my_variable_name, $*

#echo, #filter, #import

#if.. then .. else, #end, #if, #else, #end if, #unless, #for, #repeat 

#while, #break, #continue

AND SO ON. there are (many) many keywords that corresponds to the python

An example of Cheetah template language:

   #for $record in $records
   <li>record <i>$record.number</i>:
     The car mark is <b>$</b>
     Its color is <b>$</b>
     #if $

       The <a href="$">technicals data</a> are
       available for the <b>$</b> #end if
   #end for

The Python code which corresponds to this template can be devided into two parts: The first part is the namespace building and the second part is the construction of the "Template" object thanks to the namespace which has been built. Cf. the following example:

# Cheetah import
from Cheetah.Template import Template
templateFileName = 'dev/samples/car-cheetah.tmpl'

# Generate template with empty namespace      
t = Template(file=templateFileName, searchList=[{'records':[]},])
print t

namespace = {
  'records': [
    {'number': 1,
     'name': 'The first',
     'car': {'mark': 'Mehari',
             'color': 'blue',
             'technicals_data': 'cars/mehari' },
# Generate template with the above namespace
t = Template(file=templateFileName, searchList=[namespace,])
print t

This short example shows a small set of the Cheetah directives.

To conclude with Cheetah we can say that learning such a template may be a long work because the set of directives is very large.

Moreover the Cheetah template language (like DTML) allows embded code in the template, contrary to the Model-View-Controler design. The linked risk is that the template can eventually content a longer code than this in the Python script that builds the namespace (it is to say that the architecture is broken). Thus with this kind of template languages one must define strict rules for developing. Otherwise the robustness of the application will deacrease drastically.


The Page Template language (for instance Zope Page Template) is composed of two sub languages : the TAL and the METAL. The developers need to learn both of them before starting their job.

TAL and METAL offer an important set of directives, but it is possible to use them without knowing all the directives.

The TAL is made of many attributes and keywords, for example:

tal:define, tal:content, tal:attributes, tal:repeat, tal:condition,
tal:omit-tag, tal:replace, tal:on-error, tal:<myblock>
structure, nothing, not, path, string (and ${path} syntax), python, global,
nocall, exists

The METAL adds four keywords:

metal:define-macro, metal:use-marco, metal-define-slot, metal:fill-slot,  

ZPT / PT allows embded code in the template, particularly because the keyword "python" allows it. (This keyword is followed by "restricted python".)

Allowing to add python in the view permits embded code in the view, contrary to the MVC (Model-View-Controller) pattern.

Sometimes such an allowance can be difficult to understand for developers who have not been involved in this template.

zpt/pt sample:

  <li tal:repeat="record records">
    <tal:block tal:define="car record/car">
      record <i tal:content="record/number"/> : 
             <b tal:content="record/name"/><br/> 
      The car mark is <b tal:content="car/mark"/>
      Its color is <b tal:content="car/color"/>
      <div tal:condition="car/technicals_data" tal:omit-tag="">
        The <a tal:attributes="href car/technicals_data">technicals data</a>
        are available for the <b tal:content="car/color"/> 

The namespace used for this template is:

# NOTE: Zope2/lib/python must be in the path
from Products.PageTemplates.PageTemplate import PageTemplate

namespace = {
  'records': [
    {'number': 1,
     'name': 'The first',
     'car': {'mark': 'Mehari',
             'color': 'blue',
             'technicals_data': 'cars/mehari'}

source = open('sample/', 'r').read()
pt = PageTemplate()
pt.pt_edit(source, 'text/html')
print pt.pt_render(extra_context=namespace)

Developers can use ZPT/PT without the entire knowledge of the language, but practicing advanced ZPT/PT needs a long training and updating a template is more difficult if the "python keyword" is use in it.


Knowing STL needs to know : stl:repeat, stl:content, stl:if (and stl:ifnot), stl:attributes and the last stl:block.

stl sample:

     <li stl:repeat="record records">
      record <i stl:content="record/number"></i>:
             <b stl:content="record/name"></b><br/>
       The car mark is <b stl:content="record/car/mark"/>
       Its color is <b stl:content="record/car/color"/>
       <stl:block stl:if="record/car/is_technicals_data">
         The <a stl:attributes="href record/car/technicals_data">technicals
data</a> are available for the <b stl:content="record/car/color"/>

The namespace used for this example is:

from itools import get_abspath
from itools.handlers import get_handler

namespace = {
    'records': [
        {'number': '1',
         'name': 'The first',
         'car': {'mark': 'Mehari',
                 'color': 'blue',
                 'technicals_data': 'cars/mehari',
                 'is_technicals_data': True}

path = get_abspath(globals(), 'samples/car-stl.xml')
h = get_handler(path)
print h.stl(namespace)

This example covers all the keywords one needs to use STL. The same example written with PT, DTML or Cheetah, involves only a small part of the template language keywords.

The stl does not allow embded code in the template. To build the namespace, the whole developing efforts must be made in python language. As a consequence the developments are homogeneous and they respect the Model-View-Controller pattern.

An interesting point to stress is the namespace: why doesn't the stl cast the content of the namespace into a string? Because it prevents from encoding and decoding problems that can happen during XML or HTML render. As a consequence an encoding problem must be treat before processing, and a condition in stl (stl:if) must be a boolean.

This small table synthetizes the number of keywords in these four template languages:

            DTML   ZPT/PT   Cheetah  STL
Keywords    >>20   >>20     >>20     =7

Conclude on the render speed

On the one hand we have DTML, ZPT/PT and Cheetah that are made of more than twenty keywords, and on the other hand we have STL that is made of only seven keywords.

In this table we give an overview of the learning curve and the easiness of updating the template :

  Cheetah DTML ZPT STL
Easiness to fix errors 1 1 2 3
Easiness to update 1 0 2 3
Easiness to refactor 0 0 1 3
Learning curve 1 1 1 3

0: long learning, 3: short learning

Template language power overview


Where DTML can be use?

DTML can be use with Zope or theorically without Zope. It can be use with Zope thanks to DTMLFile and DTMLMethod. It can be use without of Zope (that means in Python) with DocumentTemplate.HTML and DocumentTemplate.HTMLFile. With Zope 2.7 the DocumentTemplate.HTML and DocumentTemplate.HTMLFile seems to do not work (In fact the TAL/ crash)

Positive points (Points that are frequently considered positives):

  • With dtml it is possible to build complex template page thanks to dtml-tree.
  • DTML language offers a usefull hight level API for building trees (dtml-tree) or for mail (dtml-sendmail and all the API for mail)

Negative points:

  • Not well formed XML
  • zpt vs dtml
  • Allow embded code in the view (the MVC pattern is not necessary respected)
  • Many keywords
  • The template language is not valid HTML, it cannot be used out of Zope (designers cannot read dtml file in their tool)


Where Cheetah can be use?

Cheetah can be use every where python can be use.

Positive points:

  • Cache mecanism

Negative points:

  • Not valid XML
  • Allow embded code in the view (the MVC pattern is not necessary respected)
  • Many keywords (mini-language)


Differences between ZPT and PT?

ZPT is a sub-class of PT. The main difference between ZPT and PT is the fact that PT can be use out of Zope whereas ZPT cannot.

Where ZPT / PT can be use?

ZPT can be use in Zope and only in Zope. PT can be use every where Python can be use.

Positive points:

  • Valid XML
  • the template language is valid HTML (Esay to use for designers)
  • Localisation (thanks to Localizer)

Negative points:

  • Allow embded code in the view (at the end your template is written in python, the MVC pattern is not necessary respected)

  • Many keywords

  • Build a batch without file system acces imply to used the ZUtils module in the template (tal:define="batch python:modules['ZUtils'].Batch(...)")

  • Many ways to do the same thing, for example:

    <div tal:content="here/my_content" tal:omit-tag=""></div>
    <div tal:replace="here/my_content"></div>
    <tal:my_content tal:content="here/my_content"></tal:my_content>


Where STL can be use?

STL can be use every where python can be use.

Positive points:

  • Valid XML,
  • 7(8) keywords,
  • Not allow embded code in the view (the MVC pattern is necessary respected),
  • Easy to learn,
  • The STL syntax is near to the ZPT syntax, as a consequence someone who know zpt know stl.
  • The template language is valid HTML (Esay to use for designers)
  • STL provide a powerfull localisation tool thanks to itools.i18n with the tag stl:i18n or thanks to the implicit localisation for tag 'p', 'hX', 'title'
  • The template language does less than other template language. In fact build complex template needs upstream work in python whereas it can be done in the template in other languages.
  • Keep it simple

Negative points:

  • No documentation yet available in English

This table gives an overview of template language power:

  Cheetah DTML ZPT STL
Allow embded code 0 0 1 3
Is the template valid HTML 0 0 3 3
Elegance 0 0 1 3

Allow embded code: 0: allow embded code, 3: not allow embded code

Is the template valid HTML: 0: no, 3: yes

Elegence: 0: not elegant, 3: elegant

Template language render speed

Bench methods

In this section we will deal with render speed. To calculate the render speed we use two bench methods: the first one is used to bench template language out of Zope (this method wraps the render method with a timer function which save the render time), the second one is used to bench template language in Zope (this method uses the ab (and wget) command(s)).

All the tests are realized with Zope 2.7.0 and python 2.3.3.

For the bench we use this small script that allows to wrap any methods:


We calculate the time to render a page and we do that for each template language. We will call this result "dt".

We use two simples templates pages. One without macros and one with many macros. At the end we compare the execution time for each templating language.

The first test calculates the render time out of Zope in order to stress the fastest template language in Python:

Template Language Simple template Macros template
ZPT Tal dt dt
ZPT Metal dt dt
STL dt dt

This second test measures the render time out of Zope with the use of "Restricted Python" in "Page Templage" in order to stress the impact of using "Restricted Python" in "Page Template":

Template Language Simple template Macros template
ZPT path dt dt
ZPT python dt dt
STL dt dt

The third test measures the render time in Zope (that means with the ZODB) in order to stress the impact of Zope and to stress the impact of uses "File System Directories" products like the CMF does:

Template Language Simple template Macros template
DTML dt dt
ZPT Tal dt dt
ZPT Metal dt dt
STL dt dt
ZPT Tal (FSDV) dt dt
ZPT Metal (FSDV) dt dt

In this test we use ab (or wget)

This table gives an overview of the efficient of these four template languages:

  Cheetah DTML ZPT STL
Render speed NA NA NA NA

NA: Not Yet Available


To finish we will sum up in this table all the criteria. A weight is add for each criteria in order to calculate the sum of each template languages notes. This sum will be used to help the developers to chose the template language that correspond to the developers needs. Of course the weight must be customize by developers.

  Cheetah DTML ZPT STL Weight
Allow embded code 0 0 1 3 3
Is the template valid HTML 0 0 3 3 2
Easyness to fix errors 1 1 2 3 2
Easyness to update 1 0 2 3 2
Easyness to refactor 0 0 1 3 2
Learning curve 1 1 1 3 2
Documentation 2 2 2 0 1
Render speed         1
Community 2 2 3 0 1
Elegance 0 0 1 3 1