import re
class Templite(object):
delimiter = re.compile(r"\$\{(.*?)\}\$", re.DOTALL)
def __init__(self, template):
self.tokens = self.compile(template)
@classmethod
def from_file(cls, file):
"""
loads a template from a file. `file` can be either a string, specifying
a filename, or a file-like object, supporting read() directly
"""
if isinstance(file, basestring):
file = open(file)
return cls(file.read())
@classmethod
def compile(cls, template):
tokens = []
for i, part in enumerate(cls.delimiter.split(template)):
if i % 2 == 0:
if part:
tokens.append((False, part.replace("$\\{", "${")))
else:
if not part.strip():
continue
lines = part.replace("}\\$", "}$").splitlines()
margin = min(len(l) - len(l.lstrip()) for l in lines if l.strip())
realigned = "\n".join(l[margin:] for l in lines)
code = compile(realigned, "<templite %r>" % (realigned[:20],), "exec")
tokens.append((True, code))
return tokens
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)
def emitter(*args):
for a in args: output.append(str(a))
def fmt_emitter(fmt, *args):
output.append(fmt % args)
namespace["emit"] = emitter
namespace["emitf"] = fmt_emitter
output = []
for is_code, value in __self.tokens:
if is_code:
eval(value, namespace)
else:
output.append(value)
return "".join(output)
# shorthand
__call__ = render
---------
example:
---------
>>> from templite import Templite
>>>
>>> demo = r"""
... <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>
... """
>>>
>>> t = Templite(demo)
>>> print t(x = 8)
<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>
>>>