Created
July 5, 2009 09:17
-
-
Save sma/140888 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
__all__ = ['Template', 'tag', 'filter', 'escape'] | |
def Template(source): | |
tokens = r'\{%(?:"[^"]*"|[^"]+?)*?%\}\s*|\{\{(?:"[^"]*"|[^"]+?)*?\}\}|[^{]+|\{' | |
return Block(None, iter(re.findall(tokens, source))) | |
tags, filters = {}, {} | |
def tag(cls): | |
tags[cls.__name__.lower()] = cls; return cls | |
def filter(fun): | |
filters[fun.__name__] = Filter(fun); return fun | |
class Tag(object): | |
def __init__(self, parts, tokens): | |
pass | |
def render_body(context, body): | |
return u"".join(b.render(context) for b in body) | |
class Block(Tag): | |
def __init__(self, parts, tokens): | |
self.body, self.orelse = [], []; body = self.body | |
for token in tokens: | |
if token.startswith(u"{{"): | |
body.append(Variable(token[2:-2].strip())) | |
elif token.startswith(u"{%"): | |
token = token.rstrip()[2:-2].strip() | |
if token == "end": | |
break | |
if token == "else": | |
body = self.orelse | |
continue | |
parts = re.findall(r'"[^"]*"|\S+', token) | |
body.append(tags[parts[0]](parts, tokens)) | |
else: | |
body.append(Static(token)) | |
def render(self, context): | |
return render_body(context, self.body) | |
def eval_var(source, context): | |
return eval(source, filters, context) | |
@tag | |
class If(Block): | |
def __init__(self, parts, tokens): | |
self.source = parts[1] | |
Block.__init__(self, parts, tokens) | |
def render(self, context): | |
return render_body(context, self.body if eval_var(self.source, context) else self.orelse) | |
def merge(dct1, dct2): dct1.update(dct2); return dct1 | |
@tag | |
class For(Block): | |
def __init__(self, parts, tokens): | |
self.name = parts[1] | |
assert 'in' == parts[2] | |
self.source = parts[3] | |
Block.__init__(self, parts, tokens) | |
def render(self, context): | |
items = eval_var(self.source, context) | |
if items: | |
return u"".join(render_body(merge(dict(context), {self.name: item}), self.body) for item in items) | |
else: | |
return render_body(context, self.orelse) | |
class Filter(object): | |
def __init__(self, f): self.f = f | |
def __ror__(self, other): return self.f(other) | |
@filter | |
def odd(v): return v % 2 | |
def escape(s): return unicode(s).replace(u"&", u"&").replace(u"<", u"<") | |
class Variable(object): | |
def __init__(self, source): | |
self.source = source | |
def render(self, context): | |
return escape(unicode(eval(self.source, context))) | |
class Static(object): | |
def __init__(self, text): | |
self.text = text | |
def render(self, context): | |
return self.text | |
if __name__ == '__main__': | |
s = u""" | |
Beispiel: | |
{% if len(items) %} | |
{% for i in items %} | |
{% if i|odd %}{{ i }}{% else %}-{% end %} | |
{% end %} | |
{% end %}""" | |
print Template(s).render({'items': range(10)}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment