Stack OverflowWhat is your single favorite Python templating engine?
[+51] [14] Daryl Spitzer
[2008-09-19 00:13:37]
[ python polls template-engine ]

Name your single favorite Python templating engine (and describe why it's your favorite).

[+31] [2008-09-19 00:19:52] nosklo

Jinja2 [1].

Nice syntax [2], good customization possibilities [3].

Integrates well. Can be sandboxed, so you don't have to trust completely your template authors. (Mako can't).

It is also pretty fast, with the bonus of compiling your template to bytecode and cache it, as in the demonstration below:

>>> import jinja2
>>> print jinja2.Environment().compile('{% for row in data %}{{ | upper }}{% endfor %}', raw=True) 
from __future__ import division
from jinja2.runtime import LoopContext, Context, TemplateReference, Macro, Markup, TemplateRuntimeError, missing, concat, escape, markup_join, unicode_join
name = None

def root(context, environment=environment):
    l_data = context.resolve('data')
    t_1 = environment.filters['upper']
    if 0: yield None
    for l_row in l_data:
        if 0: yield None
        yield unicode(t_1(environment.getattr(l_row, 'name')))

blocks = {}
debug_info = '1=9'

This code has been generated on the fly by Jinja2. Of course the compiler optmizes it further (e.g. removing if 0: yield None)


[+19] [2008-09-19 03:28:44] Kevin Holzer

My favorite templating engine is python itself. For example,

def index(title, content):
    return """
    """ % {'title':title, 'content':content}
def hello(name):
    return "hello %s!" % name

Nice and easy, huh?

(8) I didn't know one could use dictionaries with the % operator. I see it's there in, I just never read it closely enough. That "batteries included" functionality will often be enough for me. Thanks! - Daryl Spitzer
(14) This technique is rather limited though; there's no looping or branching, so you need to build up your response from a number of strings and then join them. - Jim
(2) @Jim : Agreed, but it's often more than enough as an alternative to require a whole template engine. It's all about awareness :) - nachik
oh, and the StringTemplate - after that, 3rd parties for more functionality. - nachik
(5) Is it considered bad practice to use % locals()? - too much php
I like this: return "hello {name}!".format(name=name) - joeriks
[+17] [2008-09-19 00:22:18] Jim

Genshi [1]. XML-based, which means your document has structure that can be manipulated rather than being an opaque string to be outputted. This enables powerful filters to be written that don't rely on regexp mangling.

It uses standard XInclude rather than custom include syntax. You can refer to other parts of the document with XPath. You can output HTML 4.01, XHTML 1.0, etc.

To sample the syntax, loops look like:

    <li py:for="item in list">${item}</li>

By default it escapes variables so you are safe from XSS attacks, and being XML-based, all output is well-formed.

It is not sandboxable yet, although there is an experimental branch that allows this.

Unlike many other template engines, it supports real Python expressions and doesn't try to stop you from doing anything. In theory, this allows you to write spaghetti code that entangles the business logic with the presentation, which is why some other template engines restrict you from doing things. In practice, this doesn't happen. Genshi treats you like an adult who can make his own decisions, much like the way Python treats you like an adult by not having private members etc.

It has good internationalisation and localisation support, being developed by the same people who work on Babel [2]. It also hooks into standard XML syntax, e.g. xml:lang rather than reinventing its own syntax.

There is persistent FUD coming from the Django camp saying that Genshi is no good for non-markup output. This is simply false, in fact the use of XML makes supporting other file formats easier. For instance, I've used Genshi to generate MIME emails with attachments, multiple parts, plain text fallbacks, etc, which was made tremendously easier by the fact that I could hook into the template structure. You can't do that with a plain text system.

And of course, if you do find an advantage to using plain text, Genshi also has a plain-text variant that can be used. For instance, I store server configuration in a database, and use Genshi's plaintext mode to generate server config files.

Benchmarks comparing Genshi performance to other template engines are available on the Genshi website [3]. Note that text mode is significantly faster than XML mode, but XML mode still performs respectably.


I haven't used any other template engines but I love how this uses XML for the templating instead of some custom solution. - Xeross
[+15] [2008-09-19 00:45:06] S.Lott

Single favorite: Mako [1]. Recently, I used Mako templates to build unit test code. The users provided the test results in spreadsheets. I used Mako to generate the Java Junit test code from the spreadsheets. Mako was easy to install and use.

I use Django a lot, but I don't have the patience to work out how to use it separately from the rest of the framework.


See… for details on how to use Django templates separately from the framework. - Daryl Spitzer
(1) Thanks! Note that it's the non-zero aspect of it. Mako is zero effort, Django is microscopic effort, but still non-zero. - S.Lott
[+13] [2008-09-21 15:54:05] Glyph

Nevow [1], of course.

from import Element, renderer
from nevow.flat import flatten
from nevow.loaders import stan
from nevow.tags import html, head, title, body, div, directive

class MyElement(Element):
    docFactory = stan(html[
            head[title["Hello, World!"]], "\n",
    def message(self, request, tag):
        tag["Hello, world!"]
        return tag
print flatten(MyElement())

Nevow has a number of unusual advantages:

  • Use Python syntax, XML templates, or tag soup. It's all the same data structures internally.
  • No code embedded in your templates. You write all your code in a real programming language (it doesn't break your debugger, your profiler, etc), but you aren't forced to write elaborate code/data bindings to make up for that. If you want to quickly inline a chunk of template in your code it's really tags.strong["quite"] convenient.
  • It's the only templating system that natively supports Twisted. Return a Deferred from your renderer if you want. It'll work.
  • Correct handling of unicode, and correct escaping of HTML entities. You don't have to think about it! If you return "<>" from a function, Nevow knows you meant the text less-than-sign greater-than-sign, not broken markup.
  • Templates can be optimized for precompilation, glomming together all non-dynamic elements at startup time rather than every render - all transparently to your application.

The main attraction, of course, is the included COMET server and JavaScript module system. You can freely inter-call between Python and JavaScript, and it's really easy:

from twisted.internet import reactor
from nevow.athena import LiveElement

class MyElement(LiveElement):
    docFactory = stan(div(render=directive('liveElement')))
    def ping(self, message):
        reactor.callLater(5.0, self.callRemote, "pong", message)

but even if all you need to output is vanilla HTML, it can be quite nice.


(1) Much of this goodness is now available as twisted.web.template, new in twisted 11.0. - Glyph
[+10] [2008-09-19 00:21:39] Clint Ecker

Django templates [1] if you're using Django or AppEngine. They way the templating engine and syntax is constructed, it's very hard to do any code in the template layer (that's a good thing). Every time I thought I was running up against a wall in Django's templates, it's because I was trying to do something wrong that I'd carried over from a more liberal templating language.

I'd also look at Jinja2 [2], its a superset of Django's templates, and therefore contains a lot more "powerful" syntax, which would be good or bad depending on how you look at it.

I've also used Cheetah [3], but once you go Django, you never go back :)

I should mention I don't like stuff like Genshi which restrict you to an XML document because its pretty inflexible when you try to do stuff that's not XML (i.e. emails, PDFs, and so forth).


(2) I have had exactly the opposite experience when it comes to outputting non-XML formats with Genshi. I know the Django documentation says that XML-based formats are bad at this, but consider the source. - Jim
(2) Genshi has a parser for text-based languages, or you can serialize XML-based languages to a text-based format. See - John Millikin
[+6] [2008-09-21 16:18:13] Armin Ronacher

I think there is no "single" template engine for Python. You have to decide that by checking your requirements. You do not work with the Django Framework? Then rule out the Django Template Engine. Do you have template designers that are not programmers or maybe not trustable (user submitted templates)? Give Jinja2 a try. Do you want to embedd the full power of Python in your templates? Mako is the engine of choice then. Are you processing XML stuff? Genshi can do that.

There are more exotic templating engines like nevow or that Markaby inspired one where I forgot the name. Cheetah/kit/TAL exist too, but they don't have any real advantages over the newer template engines listed above.

[+6] [2008-10-13 05:57:48] massimo

The web2py templating engine is full Python without indentation requirements (use pass to close blocks). Example:

 {{extend 'layout.html'}}
 {{for i in range(10):}}

It also has powerful helpers:

 {{extend 'layout.html'}}
 {{for k,link in enumerate(['']):}}
    {{=TR(TD(A(link,_href=link)),_class='odd' if k%2 else 'odd')}}

[+3] [2008-09-19 15:35:50] projecktzero

I really enjoyed using Cheetah. I liked that you could compile the templates and just populate it easily.

(2) It is also one of the fastest. - lyrae
[+2] [2008-09-19 03:44:35] habnabit

Seconding Mako. It's dead simple to set up. For most uses, this is all the code you need:

import mako.lookup
template_lookup = mako.lookup.TemplateLookup(
    directories=['/path/to/templates'], output_encoding='utf-8')
template = template_lookup.get_template('some_template.html')
rendered = template.render(c=SomeContextObject)

(1) Other templating engines are similarly easy to set up. - Nikhil Chelliah
Awesome I guess! - habnabit
[+2] [2009-09-06 18:07:23] Mario Ruggier

Evoque [1] of course: best simplicity-to-power ratio, one of the few systems that can offer sandboxed execution, possibly the fastest pure-python templating engine, small codebase, runs also on python 3, ...


[+1] [2008-11-20 02:47:34] Rob Williams

There IS a single Python templating engine: StringTemplate. If you need something more than Python itself (use it when you can), then there is no substitute. Check out for details of why.

And use it for other platforms, too. I have used StringTemplate on Python, Java, and .Net for years, and I have used many alternatives. I see no need to ever use anything else, nor can I even imagine that there could be a superior solution (no kidding). That design decision is done for me.

Care to point to where in the paper is most relevant to the discussion here? - Gregg Lind
[0] [2008-09-19 00:22:51] ADINSX

I'm a fan of Django's default templating system for its ease and simplicity.

[0] [2010-02-19 20:17:48] jjon

As a variation on the string formatting suggestion above from Kevin Holzer, here's a pretty clever hack that I found from some folks who needed to generate some KML snippets: (their code) [1]

I hate having to edit in-line html where I can't use indenting to keep track of the structure of what I'm doing, so I found it quite handy for generating little blobs of html that had to be inserted somewhere.

Using a regex for the whitespace and **kwargs for your dictionary:

def T( text, **values ):
    return re.sub( '(^\n*|\n+)[ \t]*', '', text % values )

Then you can do something like this:

    <div class="record">
        <div class="image">
            <img width=%(width)d height=%(height)d src="%(path)s" />
            <div class="name">Title: %(title)s</div>
            <div class="photog">Photographer: %(photographer)s</div>
path = "/some/path/to/image.jpg",
width = 400,
height = 600,
title = "Some Title",
photographer = "Jane Doe"

NB: Python 3 (backported to 2.6) has got: str.format(*args, **kwargs) [2]