diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2013-10-27 21:11:34 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2013-10-27 21:11:34 -0400 |
commit | fc10540c2c0a845502912a207e76fbf600dca552 (patch) | |
tree | 48b61db0e875640afdbcbaf35f4ece74cfc118c5 /coverage/templite.py | |
parent | 11a5e7225f009cab6329be456793fba4b06caab8 (diff) | |
download | python-coveragepy-fc10540c2c0a845502912a207e76fbf600dca552.tar.gz |
More templite fiddling: use locals instead of ctx[] each time.
Diffstat (limited to 'coverage/templite.py')
-rw-r--r-- | coverage/templite.py | 52 |
1 files changed, 41 insertions, 11 deletions
diff --git a/coverage/templite.py b/coverage/templite.py index 2ca12fe..088d74f 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): |