3 August 2006
Just today I pushed out the very first release of Markup, a Python-based template engine, or, as the official tagline goes, “a toolkit for stream-based generation of markup for the web.”
So… yet another template engine, I hear you say. What's next, a web framework?
(And BTW, is it just me, or is “web framework” slowly starting to replace “enterprise” as the next anti-buzzword in web development? Or maybe I'm just allergic to all the hype. Anyway…)
Truth be told, I didn't write this thing just for the fun of it. The background story is that we've been looking for a decent replacement for ClearSilver for templating in Trac for some time now. We looked at many of the potential candidates, such as Cheetah, Django templates (or the standalone equivalent Jinja) and Kid.
The requirements for a new template engine in Trac can be summarized as follows:
- No compilation of native code required. Trac users were frequently having problems getting ClearSilver up and running. We wanted a template engine written in Python to ease deployment.
- Automatic escaping of text. With most character-stream based template engines, whenever you forget to properly escape some string in the output a malicious user can find a way to abuse that as a vector for cross-site scripting (XSS) attacks. Trac had its share of such vulnerabilities in the past. Dangerous stuff, and often underestimated.
- Powerful customization capabilities. Trac users want to integrate the look and feel of their Trac deployment with the rest of their site. Having an optional header and footer template, as we currently provide, is often not enough. Customization via pure CSS can only go so far. We wanted something like XSLT to let users transform our output, but without the cruft.
- No intermediate data format. For the data passed to the template, ClearSilver expects a special data structure called the HDF (“Hierarchical Data Format”). You can't just pass it Python objects, dicts and sequences, which means that all data that should be rendered must be translated into an intermediate representation. The same is true for XSLT: you need to generate an XML/DOM-based representation of your data before you can apply an XSL transformation to it.
- Clean templates. With ClearSilver, we have to create templates that quickly become unreadable as soon as you reach a certain level of complexity - just to avoid the excessive amount of whitespace that is generated when you try to write clean, nicely indented templates. This is a real problem for pretty much any character-stream based template engine I've tried so far.
Start with those requirements, and chances are you'll end up with Kid. In fact, I was directly involved in a migration of the Trac code base to Kid this January, when DrProject, a fork of Trac “specialized for classroom use”, made the switch. Overall, I'd say it was quite successful, but we encountered a number of issues that were problematic. And those were not the kinds of problems where you just submit a patch and happily wait until it gets applied. Most of the problems were rooted deep in the design of Kid.
So instead of trying to “fix” Kid, I went with the rather unpopular route of writing a new engine from scratch, borrowing a lot of the many good ideas from Kid, but trying to get rid of the various problems. You can read up on all that on the Markup site:
- The obligatory FAQ explains why Markup is what it is and why it isn't what it's not.
- The wiki page MarkupVsKid outlines the areas where Markup is different from Kid (which are quite a few).
Anyway, if you're looking for a decent template engine, or are simply not satisfied with the one you're currently using (who is, really?) - and you're not convinced that Guido is right all the time - you may want to give Markup a try. It even comes with an implementation of the plugin API that allows you to use it directly in TurboGears and CherryPy/Buffet.
That said, this is just version 0.1. There are known issues in this release, and there's quite a bit of room for optimizing performance (we're already working on rewriting some of the core portions in C). But still, this version should be pretty usable.