views:

280

answers:

3

Hello there,

Could it be possible if somebody could help me get started in writing a python template engine? I'm new to python and as I learn the language I've managed to write a little MVC framework running in its own light-weight-WSGI-like server.

I've managed to write a script that finds and replaces keys for values: (Obviously this is not how my script is structured or implemented. this is just an example)

from string import Template

html  = '<html>\n'
html += '  <head>\n'
html += '  <title>This is so Cool : In Controller HTML</title>\n'
html += '  </head>\n'
html += '  <body>\n'
html += '    Home | <a href="/hi">Hi ${name}</a>\n'
html += '  </body>\n'
html += '<html>'

Template(html).safe_substitute(dict(name = 'Arturo'))

My next goal is to implement custom statements, modifiers, functions, etc (like 'for' loop) but i don't really know if i should use another module that i don't know about. I thought of regular expressions but i kind feel like that wouldn't be an efficient way of doing it

Any help is appreciated, and i'm sure this will be of use to other people too.

Thank you

+5  A: 

There are many powerful template languages supported by Python out there. I prefer Jinja2. Also take a look at Mako and Genshi.

Mako is fastest among three, but it's ideology allows to have a complex code logic right in template that provoke MVC principles violation from time to time.

Genshi has excellent conception, especially powerful feature is inversed template inheritance. But it is slowest among three and as practice shows all its features are often overkill in real project.

Jinja2 is a golden middle in my personal opinion. It is easily extensible, quite fast and familiar to many since it use syntax similar to Django templates and Liquid template language.

nailxx
+1 for Jinja2!!
fabrizioM
Thanks nailxx. The mako site shows this: Mako:1.10ms Myghty:4.52ms Cheetah: 1.10ms Genshi:11.46ms Django:2.74ms Kid:14.54 ms which is cool they took the time to compare engines. My brain's ability to retain information is very limited so i use conventions to help me solve that problem. I'm ok with having code logic in the in the templates as long as it's not business logic (i don't know if that's what you mean by saying "complex code logic")
jturo
A: 

Well, when I decide to play around like you did, TDD is always a good way to go.

so, why not give it a go?

for example, create a file called jturo_template.py and write:

import re
import unittest

class JTuroTemplate(object):
    u"""JTuro's template engine core class"""
    variable_regex = r'\$\{((.*)(%s)([^}]*))*\}'
    def __init__(self, string):
        self.string = string

    def __repr__(self):
        pieces = self.string.split()
        if len(pieces) > 3:
            head = "%s ..." % " ".join(pieces[:3])
        else:
            head = " ".join(pieces)

        return u'<JTuroTemplate: "%s">' % (head)

    def render(self, context):
        new = unicode(self.string)

        for key, value in context.items():
            variable_name = re.escape(key)
            regex = re.compile(self.variable_regex % variable_name)

            for match in regex.findall(new):
                if match[0]:
                    replacement = match[0].replace(key, repr(value))
                    new = new.replace('${%s}' % match[0], unicode(eval(replacement)))

        return new

class TestJTuroTemplate(unittest.TestCase):
    def test_repr(self):
        "a instance will be nicely represented"
        jt = JTuroTemplate('my template')
        self.assertEquals(repr(jt), '<JTuroTemplate: "my template">')

    def test_repr_truncated(self):
        "the python representation is truncated after 3 words"

        jt = JTuroTemplate('such a long string template')
        self.assertEquals(repr(jt), '<JTuroTemplate: "such a long ...">')

    def test_solves_simple_variables(self):
        "it solves simple variables"

        jt = JTuroTemplate('my variable is ${var} == 4')
        self.assertEquals(jt.render({'var': '4'}), 'my variable is 4 == 4')

    def test_solves_variables_with_python_code(self):
        "it solves variables with python code"

        jt = JTuroTemplate('my variable is ${var + var} == 44')
        self.assertEquals(jt.render({'var': '4'}), 'my variable is 44 == 44')

if __name__ == '__main__':
    unittest.main()

Sorry for the long post, but I think you can try this workflow:

  1. write test that fail
  2. run and watch it fail
  3. write code to make the test pass
  4. run again and watch it pass
Gabriel Falcão
thanks for your code, this will also require some studying. I thought that regular expressions was one way to go but the big question is: would a site with heavy traffic get very slow using a template engine executing lots of regular expressions?. Thanks again Gabriel for your post
jturo
A: 

well, as you said you are a python rookie, and the ONLY reason you are writting a new MVC framework and template engine from scratch, is for learning purposes, you should not care about performance IMHO.

But if you decide to put something in production, you should consider using already existent template engines, such as jinja2, mako, genshi and so on.

Anyway if you want to play around, there is a nice example of lightweight web python framework at: http://github.com/breily/juno

and lightspeed template engine at http://github.com/eklitzke/spitfire

happy hacking!

Gabriel Falcão
well I've got to admit that you got a point there. Thanks for the links and the script. Good stuff to play and learn with.
jturo