Trivial Templates
A template is used to generate a text string based on some data parameters. A simple template for a letter:
Dear ${name},
please pay us ${amount} dollars.
You use a template like this:
letterTemplate = Template(templateString) letter1 = letterTemplate(name='John', amount='100') letter2 = letterTemplate(name='Tom', amount='200')
The template can be more powerful than simple text substitution, by using loops, conditionals, etc.
Hello ${name},
you ordered these ${len(items)} items:
{{for (i, item) in enumerate(items):}}
item ${i+1}: ${item}
{{end}}
Calling the template with some parameters
print template(name='Joshua', items=['bottled water', 'milk', 'icecream'])
generates this output:
Hello Joshua, you ordered these 3 items: item 1: bottled water item 2: milk item 3: icecream
How difficult is it to implement such a template engine? 60 lines of python code:
import cStringIO as StringIO
import re
_reExpr = re.compile(r'\${(.*?)}')
_reCode = re.compile(r'(?:\n *)?{{(.*?)}}')
class Adder():
def __init__(self):
self.indent = ''
self.o = StringIO.StringIO()
def incIndent(self):
self.indent = self.indent + ' '
def decIndent(self):
self.indent = self.indent[:-2]
def write(self, txt):
self.o.write(self.indent + txt + '\n')
def getvalue(self):
value = self.o.getvalue()
self.o.close()
return value
def pairify(seq):
it = iter(seq)
while True:
yield (it.next(), it.next())
class SimpleTemplate():
def __init__(self, string, origin = None):
string = _reExpr.sub(r'{{_o(str(\1))}}', string)
parts = _reCode.split('{{}}' + string)[1:]
adder = Adder()
for (code, txt) in pairify(parts):
code = code.strip()
if code:
if code == 'end':
adder.decIndent()
else:
firstWord = code.split(None, 1)[0]
if firstWord[-1] == ':': firstWord = firstWord[:-1]
if firstWord in ['else', 'elif', 'except', 'finally']:
adder.decIndent()
adder.write(code)
if code[-1] == ':': adder.incIndent()
if txt:
txt = txt.replace('\\\\', '\\\\\\\\').replace('"', '\\\\"').replace('\\n', '\\\\n')
adder.write('_o("' + txt + '")')
source = adder.getvalue()
self.code = compile(source, origin if origin else source, 'exec')
def __call__(self, **varMap):
output = StringIO.StringIO()
varMap.update(_o = output.write)
exec self.code in varMap
content = output.getvalue()
output.close()
return content