Templating languages are hard to get right. Python has many templating languages to choose from, and their quality is above average. But they still have pretty bad problems, simply because the nature of what they're trying to do is so difficult.
I'm using Mako pretty heavily right now, mostly on my own, but at work too. It's very much beginner-friendly: for the many people, who, like myself, were familiar with Web markup and Python independently, Mako is perfect: it combines the two disparate worlds very well.
This beginner-friendliness soon gives way to hack-resistance. It's easy to get Mako to do mostly what you want it to, but when I wanted to make certain abstractions, I found it harder to do them than I expected.
As always, these are my own, probably faulty, opinions, and my or may not have any basis in reality.
So here are the problems I have with Mako:
Namespaces aren't done right, which is kind of an amalgation of these problems:
- Variables don't inherit, only functions.
- The entire namespace should inherit, not just self. In other words, get rid of self; it should be implicit (Like Django's templates).
Inherited/called body()s should be executed by default. In other words, get rid of the body() function. If you're worried about too much whitespace at the end, have it use the "trim" filter on the body() functions.
To some extent, the first two are design decisions. But they're wrong.
Namespaces Aren't Done Right
Namespaces in Mako are actually done, all told, pretty well. It keeps Python's easy packaging, and are pretty intuitive. Unfortunately, the also have their problems; inheriting only the self namespace causes unneeded complexity and repetition1 when an implicit self would work just fine.2
Variables Should Inherit
As an example, this should work:
parent.mako:
- <%
- some_var = "Hello world!";
- %>
And child.mako
<%inherit file="parent.mako" />
${ self.some_var }
## or, for that matter:
${ some_var }
## But we'll talk about that later.
Now, having variables appearing everywhere can certainly lead to hard-to-catch bugs. But this is really, really needed. I don't want to have to create a function for every little string.
And this is, after all, a templating language. This free-falling inheritance is intuitive and expected, and I was certainly surprised when I learned it wasn't accessible via either an inherited namespace or through the self object.
As a concrete example of something that is made a lot harder by not having cascading namespaces, consider this pseudo-mako-code:
parent.mako:
- <%def name="styles()">
- <link rel="stylesheet" type="text/css" href="/styles/base.css" />
- <link rel="stylesheet" type="text/css" href="/styles/${ page_name }.css" />
- </%def>
- ## HTML structure, etc
- ${ self.styles() }
- ## Rest of document, etc
child.mako:
- ## child.mako
- <%inherit file="parent.mako" />
- <%
- page_name = "child"
- %>
- ## Now a stylesheet for /styles/child.css is linked.
In reality, to get something of this effect, parent.mako's page_name has to be self.page_name(), and the child.mako has to have define function for it, and has to watch out for such trivial errors as extra whitespace:
- ## child.mako
- <%inherit file="parent.mako" />
- <%def name="page_name()" filter="trim">
- child
- </%def>
I know which one would have fulfilled the Rule of Least Surprise for me...
Entire-Namespace Inheritance
Okay, so, whenever foo.mako is rendered, I see the rendering chain going like so: if foo inherits from something, render the parent, then render foo. Foo overrides any functions or variables from it's parent, and the parent overrides the grandparent - kind of like object inheritance. If you want to refer the parent's value in your overridden section, call ${ function.parent() }, or ${ parent.function() } something of the sort.
Users of Mako will note that this is very close to what actually happens. Mako comes very close to getting it right, and certainly much closer than the competition. The difference is that you never need to call self.whatever(). Cascade the namespace, not just the functions attached to single object within to the namespace.
Child templates should be able to call parent functions at will, no self, no nothing - in fact, it shouldn't matter to the programmer whether the function is defined locally or from a parent template. This is how Django does it's block structure, and it's better for it. So would Mako be. If Django templates allowed that kind of logic in template code (unmaintainable but convenient, and indispensable for things like AJAX hacking) I'd still be using them.
Call body() by default
What is the difference between:
- <%call expr="self.function(*args)"></%call>
- ## and
- ${ self.function(*args) }
In the generated code, nothing. The second is easier to read, more concise and closer to what you wanted to say, but the generate the same code. The difference in the two is that the call generates another function, caller.body(), which the def for that particular function can access.
That's not enough reason for another tag in the language. Get rid of call, and just call functions directly; if you need to include content you can do it via parameters or the template context.
When inheriting, it's even more of a problem. You have to get next.body() done exactly right, and it's tough to do that. What I do - and what template languages should do - is to call the body of every template in the inheritance chain. Content that will be overridden should simply be defined as blocks in child templates. It's much simpler for the brain that way.
The Source Code is Messy
Now, I've tried to be generally positive about Mako, and that's cause it is a better templating language than many others. Templating, as I said at the start, is something that is hard to get right, and there's a reason that there are so many failed contenders. Mako was sufficiently close to getting it exactly as I saw the ideal that I decided to see if I could make the modifications needed.
So I downloaded and inspected the source code, and that was when I decided that it would be easier to write this instead.
I was kind of surprised, actually. After all, if Mako was the younger, smaller sibling of Myghty, surely it would have learned of the importance of maintainable code, lest Mako reach the same fate. However, the Mako source code isn't reader-friendly. I don't think I could fix the things I've described in this article without becoming intimately familiar with the Mako source, because there are large tracts in dire need of commenting, That's a problem because it means that neither I nor anyone else will probably ever get a patch done.
But while Mako certainly has a smaller codebase and smaller scope than Myghty, the code could use a lot of work, which is disturbing this early in Mako's career.
-
The specific example I have is that to include a certain mako module on every one of your templates, you need to add in a <%namespace tag to each one. Oh, but you can use inherit="True", too! Almost - I shouldn't need to explicitly specify that, and then I shouldn't have to refer to it from self.foo.whatever(). I should be able to do <%namespace (whatever) in one of my parent templates, then refer to whatever.func() in all child templates. ↩
-
Don't get me wrong, I like Python's explicit "self." I think it's far better than the alternatives: an implicit self, magic, or syntax. Explicit is better than implicit. However, the whole point of using a DSL - like a templating language - is that you can add in syntax and implicit logic without majorly messing up the rest of the application. ↩
Comments
335 spam comments omitted.
I am no longer accepting new comments.
Anonymous
#913, 2007-11-17T18:53:24Z
adam,
i've been reading your blog since i found it in search for something else.
dude, it's not mako problem that you fail to understand basic things that try to make your life easier. what you call a "variable inheritance" is usually called one-global-namespace-hell. if some function needs parameters from caller it must use parameters. period. end of story.
<%def name="styles(page_name)">
next. "Call body() by default". again, you just fail to understand what <%call> tag is for. generally, it is not equal to ${ func() }. RTFM - everything is explained there pretty clear.
i'm really sorry for being rude but your whole blog is filled up with blames on this and that based on your incompetence in subject.
alex
#914, 2007-11-17T18:55:48Z
adam,
i've been reading your blog since i found it in search for something else.
dude, it's not mako problem that you fail to understand basic things that try to make your life easier. what you call a "variable inheritance" is usually called one-global-namespace-hell. if some function needs parameters from caller it must use parameters. period. end of story.
<%def name="styles(page_name)">
next. "Call body() by default". again, you just fail to understand what <%call> tag is for. generally, it is not equal to ${ func() }. RTFM - everything is explained there pretty clear.
i'm really sorry for being rude but your whole blog is filled up with blames on this and that based on your incompetence in subject.