+#!/usr/bin/env python
+#
+# Templite+
+# A light-weight, fully functional, general purpose templating engine
+#
+# Copyright (c) 2009 joonis new media
+# Author: Thimo Kraemer <thimo.kraemer@joonis.de>
+#
+# Based on Templite - Tomer Filiba
+# http://code.activestate.com/recipes/496702/
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+import sys, re
+
+class Templite(object):
+ auto_emit = re.compile('(^[\'\"])|(^[a-zA-Z0-9_\[\]\'\"]+$)')
+
+ def __init__(self, template, start='${', end='}$'):
+ if len(start) != 2 or len(end) != 2:
+ raise ValueError('each delimiter must be two characters long')
+ delimiter = re.compile('%s(.*?)%s' % (re.escape(start), re.escape(end)), re.DOTALL)
+ offset = 0
+ tokens = []
+ for i, part in enumerate(delimiter.split(template)):
+ part = part.replace('\\'.join(list(start)), start)
+ part = part.replace('\\'.join(list(end)), end)
+ if i % 2 == 0:
+ if not part: continue
+ part = part.replace('\\', '\\\\').replace('"', '\\"')
+ part = '\t' * offset + 'emit("""%s""")' % part
+ else:
+ part = part.rstrip()
+ if not part: continue
+ if part.lstrip().startswith(':'):
+ if not offset:
+ raise SyntaxError('no block statement to terminate: ${%s}$' % part)
+ offset -= 1
+ part = part.lstrip()[1:]
+ if not part.endswith(':'): continue
+ elif self.auto_emit.match(part.lstrip()):
+ part = 'emit(%s)' % part.lstrip()
+ lines = part.splitlines()
+ margin = min(len(l) - len(l.lstrip()) for l in lines if l.strip())
+ part = '\n'.join('\t' * offset + l[margin:] for l in lines)
+ if part.endswith(':'):
+ offset += 1
+ tokens.append(part)
+ if offset:
+ raise SyntaxError('%i block statement(s) not terminated' % offset)
+ self.__code = compile('\n'.join(tokens), '<templite %r>' % template[:20], 'exec')
+
+ def render(self, __namespace=None, **kw):
+ """
+ renders the template according to the given namespace.
+ __namespace - a dictionary serving as a namespace for evaluation
+ **kw - keyword arguments which are added to the namespace
+ """
+ namespace = {}
+ if __namespace: namespace.update(__namespace)
+ if kw: namespace.update(kw)
+ namespace['emit'] = self.write
+
+ __stdout = sys.stdout
+ sys.stdout = self
+ self.__output = []
+ eval(self.__code, namespace)
+ sys.stdout = __stdout
+ return ''.join(self.__output)
+
+ def write(self, *args):
+ for a in args:
+ self.__output.append(str(a))
+
+
+if __name__ == '__main__':
+
+ if '--demo' not in sys.argv:
+ vars = eval('dict(' + ' '.join(sys.argv[1:]) + ')')
+ sys.stdout.write(Templite(sys.stdin.read()).render(vars))
+ sys.exit(0)
+
+ template = r"""
+This we already know:
+<html>
+ <body>
+ ${
+ def say_hello(arg):
+ emit("hello ", arg, "<br>")
+ }$
+
+ <table>
+ ${
+ for i in range(10):
+ emit("<tr><td> ")
+ say_hello(i)
+ emit(" </tr></td>\n")
+ }$
+ </table>
+
+ ${emit("hi")}$
+
+ tralala ${if x > 7:
+ say_hello("big x")}$ lala
+
+ $\{this is escaped starting delimiter
+
+ ${emit("this }\$ is an escaped ending delimiter")}$
+
+ ${# this is a python comment }$
+
+ </body>
+</html>
+
+But this is completely new:
+${if x > 7:}$
+ x is ${emit('greater')}$ than ${print x-1}$ Well, the print statement produces a newline.
+${:else:}$
+ This terminates the previous code block and starts an else code block
+ Also this would work: $\{:end}\$$\{else:}\$, but not this: $\{:end}\$ $\{else:}\$
+${:this terminates the else-block
+only the starting colon is essential}$
+
+So far you had to write:
+${
+ if x > 3:
+ emit('''
+ After a condition you could not continue your template.
+ You had to write pure python code.
+ The only way was to use %%-based substitutions %s
+ ''' % x)
+}$
+
+${if x > 6:}$
+ Now you do not need to break your template ${print x}$
+${:elif x > 3:}$
+ This is great
+${:endif}$
+
+${for i in range(x-1):}$ Of course you can use any type of block statement ${i}$ ${"fmt: %s" % (i*2)}$
+${:else:}$
+Single variables and expressions starting with quotes are substituted automatically.
+Instead $\{emit(x)}\$ you can write $\{x}\$ or $\{'%s' % x}\$ or $\{"", x}\$
+Therefore standalone statements like break, continue or pass
+must be enlosed by a semicolon: $\{continue;}\$
+The end
+${:end-for}$
+"""
+
+ t = Templite(template)
+ print t.render(x=8)
+
+
+ # Output is:
+ """
+This we already know:
+<html>
+ <body>
+
+
+ <table>
+ <tr><td> hello 0<br> </tr></td>
+<tr><td> hello 1<br> </tr></td>
+<tr><td> hello 2<br> </tr></td>
+<tr><td> hello 3<br> </tr></td>
+<tr><td> hello 4<br> </tr></td>
+<tr><td> hello 5<br> </tr></td>
+<tr><td> hello 6<br> </tr></td>
+<tr><td> hello 7<br> </tr></td>
+<tr><td> hello 8<br> </tr></td>
+<tr><td> hello 9<br> </tr></td>
+
+ </table>
+
+ hi
+
+ tralala hello big x<br> lala
+
+ ${this is escaped starting delimiter
+
+ this }$ is an escaped ending delimiter
+
+
+
+ </body>
+</html>
+
+But this is completely new:
+
+ x is greater than 7
+ Well, the print statement produces a newline.
+
+
+So far you had to write:
+
+ After a condition you could not continue your template.
+ You had to write pure python code.
+ The only way was to use %-based substitutions 8
+
+
+
+ Now you do not need to break your template 8
+
+
+
+ Of course you can use any type of block statement 0 fmt: 0
+ Of course you can use any type of block statement 1 fmt: 2
+ Of course you can use any type of block statement 2 fmt: 4
+ Of course you can use any type of block statement 3 fmt: 6
+ Of course you can use any type of block statement 4 fmt: 8
+ Of course you can use any type of block statement 5 fmt: 10
+ Of course you can use any type of block statement 6 fmt: 12
+
+Single variables and expressions starting with quotes are substituted automatically.
+Instead ${emit(x)}$ you can write ${x}$ or ${'%s' % x}$ or ${"", x}$
+Therefore standalone statements like break, continue or pass
+must be enlosed by a semicolon: ${continue;}$
+The end
+"""
+