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, "" % (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""" ... ... ... ${ ... def say_hello(arg): ... emit("hello ", arg, "
") ... }$ ... ... ... ${ ... for i in range(10): ... emit("\n") ... }$ ...
") ... say_hello(i) ... emit("
... ... ${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 }$ ... ... ... ... """ >>> >>> t = Templite(demo) >>> print t(x = 8)
hello 0
hello 1
hello 2
hello 3
hello 4
hello 5
hello 6
hello 7
hello 8
hello 9
hi tralala hello big x
lala ${this is escaped starting delimiter this }$ is an escaped ending delimiter >>>