Feed Sign in with OpenID OpenID

Simon Willison’s Weblog

Ludicrously simple templates with Python

A long, long time ago I wrote my first ever PHP templating system. It was pretty simple; it consisted of a function that took two arguments: the name of a template file, and an associative array of replacements to make on that file.

I’ve finally got around to playing with Python CGIs for web development recently, and decided I needed a similar system. Thanks to Python’s powerful string formatting operator, it ended up as a one-liner:

def template(file, vars):
    return open(templatedir.template, 'r').read() % vars

Presuming you’ve set templatedir at the top of the script, the above function lets you load a template and make some simple replacements on it with a single function call. For example:

<h3>%(title)s</h3>

%(body)s

<p class="footer">Posted: %(date)s</p>

With the above saved in the template directory as “entry.tpl”, the template function above can be used thus:


print template('entry.tpl', {
    'title':'A blog entry', 
    'body':'Entry goes here...',
    'date':'3rd July 2003'})

The work is all done by the % vars bit at the end of the line. Since vars is a dictionary, Python substitutes the named items in the dictionary for their corresponding %(varname)s tokens in the string loaded from the template file. More information on string formatting operations can be found in the manual.

As templating systems go, it’s far from the most useful or complete solution. It does however show that a little Python can go quite a long way.

This is Ludicrously simple templates with Python by Simon Willison, posted on 28th July 2003.

View blog reactions

Next: Better web forms

Previous: More CSS tips and tricks

12 comments

  1. Python in language I can understand! More, more!

    tim martens - 29th July 2003 00:43 - #

  2. We JUST learned about this sort of variable interpolation in my Python class. It was touched on briefly and was curious of a larger example. Nice.

    Rob Hudson - 29th July 2003 06:27 - #

  3. Hi Simon!

    f you define the function like this:

    def template(file, **vars):

    you can call it this way:

    print template('entry.tpl', title="A blog entry", body="Entry goes here...", date="3rd July 2003")

    The ** modifier on vars collects all remaining parameters into the dictionary.

    Beat Bolli - 29th July 2003 07:37 - #

  4. Beat: that's pretty neat, I'd forgotten you could set up variable argument functions like that. I prefer the dictionary method though as it makes it easier to hand groups of parameters around. I'm sure you could combine the two approaches with a bit of extra logic though.

    Simon Willison - 29th July 2003 13:18 - #

  5. If you're interested in something with more features, but the same basic concept in practice, Zope's DTML is worth a look.

    It has controls such as IF/ELSE and IN, default values for variables and (though discouraged) the ability to include arbitrary Python code (using the EXPR attr).

    Joe Grossberg - 29th July 2003 17:09 - #

  6. Shameless plug:

    If you are interested in a pure Python templating engine that is a bit easier to grasp than DTML and still very featureful, check out my website. The engine is called cmTemplate and has very complete documentation.

    Yep, it's a plug, and it is certainly shameless. Sorry ;-)

    C

    Chris Monson - 19th August 2003 22:02 - #

  7. Well, you could complicate it enormously, and do this to get "both" of the exciting options:

    def template(file, vars={}, **extras):
    if extras: vars.update(extras)
    return open(templatedir.template, 'r').read() % vars

    This way you can pass in some pre-defined things a'la a dict, and then throw in a few changes that could override it as well. Please excuse the bad formatting, but I couldn't find any combination of tags that would allow me to embed the spacing (entity-references didn't work, pre isn't allowed).

    petrilli - 7th October 2003 15:24 - #

  8. I'm with Simon on this. Had completely forgotten this technique. Infact I came here after having evaluated some 'template engines' with 10 page documentation (I guess they forgot this technique as well :-)). Thanx for the whack on the side of the head.

    alankar - 8th October 2003 14:12 - #

  9. Re: DTML

    Or, the newer fancier Zope Page Templates. There are a few implementations out there, not all of them need Zope at all (mine, for instance :).

    In any case, ZPT is really, really nice. <h1 tal:content='foo'>Value of foo goes here</h1> does what you would expect, and all that pesky HTML character quoting is done for you. Of course it also interpolates into tag attributes, and has all the control structures you would expect.

    Sune Kirkeby - 4th December 2003 08:48 - #

  10. Shouldn't that be: return open(templatedir.file,....

    Bill Seitz - 20th December 2003 04:49 - #

  11. petrilli - note that your code:

    def template(file, vars={}, **extras):
        if extras:
            vars.update(extras)
        ... % vars
    

    has a combination of two small bugs:

    • vars.update is modifying the dictionary passed to you, which might be an unpleasant surprise to the code that calls this function;
    • if it's called without a vars argument, the same dictionary object (created by {} when the function was defined) is used every time and it will accumulate values.

    It means that if you do: template('f', foo='fred') and then template('f', bar='barney'), at the second time vars is {'foo': 'fred', 'bar': 'barney'} keys...

    In this scenario it's a pretty harmless bug; I just wanted to illuminte this corner of Python. The right way is to copy the argument:

    def template(file, vars={}, **extras):
        vars = dict(vars)  # or vars.copy()
        vars.update(extras)  # no-op if extras=={}, no if needed
        ... % vars
    

    And in Python 2.3 and later you can let dict do the merging:

    def template(file, vars={}, **extras):
        ... % dict(vars, **extras)
    

    Beni Cherniavsky - 17th March 2005 23:00 - #

  12. its quite helpful, it help me as well.

    Outsourcing - 8th March 2006 07:14 - #

Comments are closed.

Previously hosted at http://simon.incutio.com/archive/2003/07/28/simpleTemplates

A django site