diff options
author | sbc <sbc@chromium.org> | 2014-11-24 09:25:29 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-11-24 17:25:51 +0000 |
commit | 0cec9d79bb20fa7706eed91c3671ac507ba375e7 (patch) | |
tree | 679ed6278fe47e8bc4df89759dc39428dd970f88 /third_party/pycoverage/coverage/templite.py | |
parent | 698913910433d3d3a47b36360f1c583ff9d92f05 (diff) | |
download | chromium_src-0cec9d79bb20fa7706eed91c3671ac507ba375e7.zip chromium_src-0cec9d79bb20fa7706eed91c3671ac507ba375e7.tar.gz chromium_src-0cec9d79bb20fa7706eed91c3671ac507ba375e7.tar.bz2 |
Add python coverage module to third_party
Also add a top level Makefile to native_client_sdk which
excersises this.
Add exception to checklicenses.py since upstream does
not include license information in their source files.
BUG=435348,434551
Review URL: https://codereview.chromium.org/727003004
Cr-Commit-Position: refs/heads/master@{#305439}
Diffstat (limited to 'third_party/pycoverage/coverage/templite.py')
-rw-r--r-- | third_party/pycoverage/coverage/templite.py | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/third_party/pycoverage/coverage/templite.py b/third_party/pycoverage/coverage/templite.py new file mode 100644 index 0000000..e5c0baf --- /dev/null +++ b/third_party/pycoverage/coverage/templite.py @@ -0,0 +1,208 @@ +"""A simple Python template renderer, for a nano-subset of Django syntax.""" + +# Coincidentally named the same as http://code.activestate.com/recipes/496702/ + +import re + +from coverage.backward import set # pylint: disable=W0622 + + +class CodeBuilder(object): + """Build source code conveniently.""" + + def __init__(self, indent=0): + self.code = [] + self.indent_amount = indent + + def add_line(self, line): + """Add a line of source to the code. + + Don't include indentations or newlines. + + """ + self.code.append(" " * self.indent_amount) + 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 + + def dedent(self): + """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 = str(self) + exec(code_text, g) + return g[fn_name] + + +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) + + # We construct a function in source form, then compile it and hold onto + # 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("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) + + ops_stack = [] + for tok in toks: + if tok.startswith('{{'): + # An expression to evaluate. + 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. + assert len(words) == 2 + ops_stack.append('if') + code.add_line("if %s:" % self.expr_code(words[1])) + code.indent() + elif words[0] == 'for': + # 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 c_%s in %s:" % ( + words[1], + self.expr_code(words[3]) + ) + ) + code.indent() + elif words[0].startswith('end'): + # Endsomething. Pop the ops stack + end_what = words[0][3:] + if ops_stack[-1] != end_what: + raise SyntaxError("Mismatched end tag: %r" % end_what) + ops_stack.pop() + code.dedent() + else: + raise SyntaxError("Don't understand tag: %r" % words[0]) + else: + # Literal content. If it isn't empty, output it. + if 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]) + + code.add_line("return ''.join(result)") + code.dedent() + self.render_function = code.get_function('render') + + def expr_code(self, expr): + """Generate a Python expression for `expr`.""" + if "|" in expr: + pipes = expr.split("|") + code = self.expr_code(pipes[0]) + for func in pipes[1:]: + 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: + self.all_vars.add(expr) + code = "c_%s" % expr + return code + + 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) + return self.render_function(ctx, self.do_dots) + + def do_dots(self, value, *dots): + """Evaluate dotted expressions at runtime.""" + for dot in dots: + try: + value = getattr(value, dot) + except AttributeError: + value = value[dot] + if hasattr(value, '__call__'): + value = value() + return value |