04
Oct
09

PyML(Python Markup Language) template engine

I know it’s been a while since my last post. I’ve been busy with setting up and pulling down pepijndevos.nl(after I decided to go for another blog system), I’ve spend a good deal of time working on PyMouse and worked on some projects for myself and for clients.

I want to share one of those projects with you. I decided to make a CMS like Jekyll and Hyde(not the book!), since I liked neither of the previous systems. So like a lot of people who like reinventing wheels I started to look for a nice markup language for the user and a nice template language for the designer.

I choose for Markdown right away, end of story here…

Because of the little experience I have with Pylons I looked ad Genshi and Mako initially(can’t help the Chinese feeling these names give me). Personally the ugly tags in most template languages make me run away, so I threw Make out of the window, together with all the other ugly-tag-based languages.

I like the idea of working with valid XML attributes for templating, so I started of with Genshi initially, especially since speed isn’t a major point for a static blog!

Days later I came across this wiki page on the Python website: http://wiki.python.org/moin/Templating

Looking at Dirty I remembered my own attempts to write a template language implemented as Python objects(extending dict, for free functionality).

The problem with writing such a thing is described in the code below, I mailed the developer of Dirty about how he solved the problem — he didn’t.

>>> def div(*content, **attributes):
...     pass
...
>>> div("hello world!", id="test") # Doesn't html have the attributes first!? Ugly!
>>> def div(**attributes, *content): # Not allowed!
  File "", line 1
    def div(**attributes, *content):
                        ^
SyntaxError: invalid syntax
>>> def div(atributes, *content):
...     pass
...
>>> div({'id':'test'}, "hello world!") # Even uglier!

But then I came up with this slightly brilliant solution:

When you define __call__ on a class it allows you to call the class, no big deal, eh? Wrong! It allows you to write this:

html(xmlns="http://www.w3.org/1999/xhtml")(
    head( # No atributes
        title("Hello world")
    ),
    body(
        p(id="test")(
            "Hi", br(),
            "How are you?"
        ),
        img(src="test.img", alt="just a test") # No content
        ul(
            *[li(c) for c in xrange(10)] # Now that is cool!
        )
    )
)

In short PyML is a pure Python combination of a string and a dict looking like html when written and printed!

You can do all sorts of things with this that you normally do with dictionaries, strings and lists, like sorting and filtering them, change a p into a div or getting/setting attributes later with square brackets. You could define functions to return a snippet or you could include templates into others. If you’re finished with them, just print the object!

Oh, about the speed? Quite good for my hobby project! (Unreliable micro benchmark ahead!)

Render time Engine
0.03160 PyML
0.50618 Genshi
0.01813 Mako

You can get it here if you want! Or wait for my complete CMS of course…

Advertisements

7 Responses to “PyML(Python Markup Language) template engine”


  1. October 16, 2009 at 7:07 am

    Ha! Excellent, looks interesting – did you see one of my solutions for “pumping” the namespace to have the objects you’d need? I like not polluting the global space – for a template that might not be an issue, since your framework should have full control over it.

    My solution was to backtrack down a level and fill in the locals of whatever was calling the “pump” namespace, should be in that thread, if not email me I’ll hook ya up

  2. 2 pepijndevos
    October 16, 2009 at 8:48 am

    I have some more tricks to try out, but yours is definitely one of them. Another one is to use the with statement anyway, and use those setup and breakdown methods to ‘pollute’ the namespace and clean it up. Also the __getattribute__ trick you used is something to play with. Maybe I could change the __getattr__ of __main__?

  3. January 10, 2010 at 3:23 pm

    A good tutorial to be applied. thank you very much boss

  4. April 14, 2010 at 7:37 am

    Funny. Yesterday when trying to figure out an alternative to XML using pure Python syntax I came up with something almost identical. The difference was that instead of __call__ I used __getitem__, so the code looks like this instead:
    something(foo=”bar”) [
    more(stuff = 1),
    more(stuff = 2),
    end(donald = 4) [ more(stuff = 3) ]
    ]
    for example. The reason I used [] instead of () is that, for me, it made it easier to for the eye to parse the text.

    Makes you wonder how many more out there that has expressed HTML/XML/etc using pure Python this way. 🙂

  5. 5 pepijndevos
    April 14, 2010 at 12:49 pm

    I know of at least 2 others, but they did not solve the kwargs problem.

    A few questions about yours:

    Can you add any number of arguments to a __getitem__ call?

    How would the code for an item without attributes look?

    Can I see your code? I’m curious to how you solved certain issues. Where are ‘more’, ‘something’ and ‘end’ defined for example?

  6. April 14, 2010 at 2:22 pm

    Yes, __getitem__ can have any number of arguments. As for the item without attributes, see the code belov. It’s just a prototype I hacked together, but I guess it works as illustration. Here’s the code:

    class something:
    def __init__(self, **kwargs):
    self.kwargs = kwargs

    def __getitem__(self, *args):
    self.args = args
    return self

    def __repr__(self):
    return “something(” + repr(self.kwargs) + “, ” + repr(self.args) + “)”

    class more:
    def __init__(self, **kwargs):
    self.kwargs = kwargs

    def __repr__(self):
    return “more(” + repr(self.kwargs) + “)”

    class end:
    def __init__(self, **kwargs):
    self.kwargs = kwargs

    def __getitem__(self, *args):
    self.args = args
    return self

    def __repr__(self):
    return “end(” + repr(self.kwargs) + “, ” + repr(self.args) + “)”

    class __noattr__:
    def __getitem__(self, *args):
    self.args = args
    return self

    def __repr__(self):
    return “noattr(” + repr(self.args) + “)”

    noattr = __noattr__()

    s = something(foo=”bar”) [
    more(stuff=1),
    noattr [ more(stuff=2) ],
    end(donald=4) [ more(stuff=3) ]
    ]

    print s
    — end code —

    As you can see, everything is defined at the top namespace. This is probably not ideal, but should be solvable I think (at least by someone who knows more python than I do. :)).

  7. April 14, 2010 at 2:30 pm

    Just realized that the ‘noattr’ instance must be immuatble, so the __noattr__.__getitem__ function should not modify the instance but return a cloned (and modified) instance. Otherwise the data structure will not be correct when more than one ‘noattr’ is used.


Comments are currently closed.

My blog has moved!

My blog has permanently moved to a self hosted Wordpress at http://pepijndevos.nl

This blog will stay around for accidental search engine visitors.

Me

This is me

Blog Stats

  • 22,355 hits

@PepijnDeVos


%d bloggers like this: