summaryrefslogtreecommitdiff
path: root/coverage/templite.py
diff options
context:
space:
mode:
Diffstat (limited to 'coverage/templite.py')
-rw-r--r--coverage/templite.py58
1 files changed, 32 insertions, 26 deletions
diff --git a/coverage/templite.py b/coverage/templite.py
index f61fbdca..c39e061e 100644
--- a/coverage/templite.py
+++ b/coverage/templite.py
@@ -2,46 +2,46 @@
# Coincidentally named the same as http://code.activestate.com/recipes/496702/
-import re
+import re, sys
class Templite(object):
"""A simple template renderer, for a nano-subset of Django syntax.
Supported constructs are extended variable access::
-
+
{{var.modifer.modifier|filter|filter}}
-
+
loops::
-
+
{% for var in list %}...{% endfor %}
-
+
and ifs::
-
+
{% if var %}...{% endif %}
Comments are within curly-hash markers::
-
+
{# This will be ignored #}
Construct a Templite with the template text, then use `render` against a
dictionary context to create a finished string.
-
+
"""
def __init__(self, text, *contexts):
"""Construct a Templite with the given `text`.
-
+
`contexts` are dictionaries of values to use for future renderings.
These are good for filters and global values.
-
+
"""
self.text = text
self.context = {}
for context in contexts:
self.context.update(context)
-
+
# Split the text to form a list of tokens.
toks = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text)
-
+
# Parse the tokens into a nested list of operations. Each item in the
# list is a tuple with an opcode, and arguments. They'll be
# interpreted by TempliteEngine.
@@ -80,47 +80,53 @@ class Templite(object):
ops = ops_stack.pop()
assert ops[-1][0] == words[0][3:]
else:
- raise Exception("Don't understand tag %r" % words)
+ raise SyntaxError("Don't understand tag %r" % words)
else:
ops.append(('lit', tok))
-
+
assert not ops_stack, "Unmatched action tag: %r" % ops_stack[-1][0]
self.ops = ops
def render(self, context=None):
"""Render this template by applying it to `context`.
-
+
`context` is a dictionary of values to use in this rendering.
-
+
"""
# Make the complete context we'll use.
ctx = dict(self.context)
if context:
ctx.update(context)
-
+
# Run it through an engine, and return the result.
engine = _TempliteEngine(ctx)
engine.execute(self.ops)
- return engine.result
+ return "".join(engine.result)
class _TempliteEngine(object):
"""Executes Templite objects to produce strings."""
def __init__(self, context):
self.context = context
- self.result = ""
+ self.result = []
def execute(self, ops):
"""Execute `ops` in the engine.
-
+
Called recursively for the bodies of if's and loops.
-
+
"""
for op, args in ops:
if op == 'lit':
- self.result += args
+ self.result.append(args)
elif op == 'exp':
- self.result += str(self.evaluate(args))
+ try:
+ self.result.append(str(self.evaluate(args)))
+ except:
+ exc_class, exc, _ = sys.exc_info()
+ new_exc = exc_class("Couldn't evaluate {{ %s }}: %s"
+ % (args, exc))
+ raise new_exc
elif op == 'if':
expr, body = args
if self.evaluate(expr):
@@ -132,13 +138,13 @@ class _TempliteEngine(object):
self.context[var] = val
self.execute(body)
else:
- raise Exception("TempliteEngine doesn't grok op %r" % op)
+ raise AssertionError("TempliteEngine doesn't grok op %r" % op)
def evaluate(self, expr):
"""Evaluate an expression.
-
+
`expr` can have pipes and dots to indicate data access and filtering.
-
+
"""
if "|" in expr:
pipes = expr.split("|")