summaryrefslogtreecommitdiff
path: root/coverage/templite.py
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2013-10-27 21:11:34 -0400
committerNed Batchelder <ned@nedbatchelder.com>2013-10-27 21:11:34 -0400
commit492029741a5f9227a789701360b3b9f1f7e4a9f4 (patch)
tree802d3df95d98c5d964d07835b4190f65dda6a23c /coverage/templite.py
parent8c275cd9b8817d0a817ebce853c111d11ceb86e9 (diff)
downloadpython-coveragepy-git-492029741a5f9227a789701360b3b9f1f7e4a9f4.tar.gz
More templite fiddling: use locals instead of ctx[] each time.
Diffstat (limited to 'coverage/templite.py')
-rw-r--r--coverage/templite.py52
1 files changed, 41 insertions, 11 deletions
diff --git a/coverage/templite.py b/coverage/templite.py
index 2ca12fef..088d74f1 100644
--- a/coverage/templite.py
+++ b/coverage/templite.py
@@ -8,9 +8,9 @@ import re
class CodeBuilder(object):
"""Build source code conveniently."""
- def __init__(self):
+ def __init__(self, indent=0):
self.code = []
- self.indent_amount = 0
+ self.indent_amount = indent
def add_line(self, line):
"""Add a line of source to the code.
@@ -22,6 +22,12 @@ class CodeBuilder(object):
self.code.append(line)
self.code.append("\n")
+ def add_section(self):
+ """Add a section, a sub-CodeBuilder."""
+ sect = CodeBuilder(self.indent_amount)
+ self.code.append(sect)
+ return sect
+
def indent(self):
"""Increase the current indent for following lines."""
self.indent_amount += 4
@@ -30,11 +36,14 @@ class CodeBuilder(object):
"""Decrease the current indent for following lines."""
self.indent_amount -= 4
+ def __str__(self):
+ return "".join(str(c) for c in self.code)
+
def get_function(self, fn_name):
"""Compile the code, and return the function `fn_name`."""
assert self.indent_amount == 0
g = {}
- code_text = "".join(self.code)
+ code_text = str(self)
exec(code_text, g)
return g[fn_name]
@@ -75,13 +84,27 @@ class Templite(object):
self.context.update(context)
# We construct a function in source form, then compile it and hold onto
- # it, and execute it to create the template output.
+ # it, and execute it to render the template.
code = CodeBuilder()
code.add_line("def render(ctx, dot):")
code.indent()
+ vars_code = code.add_section()
+ self.all_vars = set()
+ self.loop_vars = set()
code.add_line("result = []")
- code.add_line("r = result.append")
+ code.add_line("a = result.append")
+ code.add_line("e = result.extend")
+ code.add_line("s = str")
+
+ buffered = []
+ def flush_output():
+ """Force `buffered` to the code builder."""
+ if len(buffered) == 1:
+ code.add_line("a(%s)" % buffered[0])
+ elif len(buffered) > 1:
+ code.add_line("e([%s])" % ",".join(buffered))
+ del buffered[:]
# Split the text to form a list of tokens.
toks = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text)
@@ -90,12 +113,13 @@ class Templite(object):
for tok in toks:
if tok.startswith('{{'):
# An expression to evaluate.
- code.add_line("r(str(%s))" % self.expr_code(tok[2:-2].strip()))
+ buffered.append("s(%s)" % self.expr_code(tok[2:-2].strip()))
elif tok.startswith('{#'):
# Comment: ignore it and move on.
continue
elif tok.startswith('{%'):
# Action tag: split into words and parse further.
+ flush_output()
words = tok[2:-2].strip().split()
if words[0] == 'if':
# An if statement: evaluate the expression to determine if.
@@ -107,8 +131,9 @@ class Templite(object):
# A loop: iterate over expression result.
assert len(words) == 4 and words[2] == 'in'
ops_stack.append('for')
+ self.loop_vars.add(words[1])
code.add_line(
- "for ctx[%r] in %s:" % (
+ "for c_%s in %s:" % (
words[1],
self.expr_code(words[3])
)
@@ -126,7 +151,11 @@ class Templite(object):
else:
# Literal content. If it isn't empty, output it.
if tok:
- code.add_line("r(%r)" % tok)
+ buffered.append("%r" % tok)
+ flush_output()
+
+ for var_name in self.all_vars - self.loop_vars:
+ vars_code.add_line("c_%s = ctx[%r]" % (var_name, var_name))
if ops_stack:
raise SyntaxError("Unmatched action tag: %r" % ops_stack[-1])
@@ -141,14 +170,16 @@ class Templite(object):
pipes = expr.split("|")
code = self.expr_code(pipes[0])
for func in pipes[1:]:
- code = "ctx[%r](%s)" % (func, code)
+ self.all_vars.add(func)
+ code = "c_%s(%s)" % (func, code)
elif "." in expr:
dots = expr.split(".")
code = self.expr_code(dots[0])
args = [repr(d) for d in dots[1:]]
code = "dot(%s, %s)" % (code, ", ".join(args))
else:
- code = "ctx[%r]" % expr
+ self.all_vars.add(expr)
+ code = "c_%s" % expr
return code
def render(self, context=None):
@@ -161,7 +192,6 @@ class Templite(object):
ctx = dict(self.context)
if context:
ctx.update(context)
-
return self.render_function(ctx, self.do_dots)
def do_dots(self, value, *dots):