diff options
Diffstat (limited to 'coverage/templite.py')
-rw-r--r-- | coverage/templite.py | 58 |
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("|") |