Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: third_party/coverage/templite.py

Issue 63813002: Add python coverage 3.7 to depot tools. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/coverage/summary.py ('k') | third_party/coverage/version.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 """A simple Python template renderer, for a nano-subset of Django syntax."""
2
3 # Coincidentally named the same as http://code.activestate.com/recipes/496702/
4
5 import re, sys
6
7 class Templite(object):
8 """A simple template renderer, for a nano-subset of Django syntax.
9
10 Supported constructs are extended variable access::
11
12 {{var.modifer.modifier|filter|filter}}
13
14 loops::
15
16 {% for var in list %}...{% endfor %}
17
18 and ifs::
19
20 {% if var %}...{% endif %}
21
22 Comments are within curly-hash markers::
23
24 {# This will be ignored #}
25
26 Construct a Templite with the template text, then use `render` against a
27 dictionary context to create a finished string.
28
29 """
30 def __init__(self, text, *contexts):
31 """Construct a Templite with the given `text`.
32
33 `contexts` are dictionaries of values to use for future renderings.
34 These are good for filters and global values.
35
36 """
37 self.text = text
38 self.context = {}
39 for context in contexts:
40 self.context.update(context)
41
42 # Split the text to form a list of tokens.
43 toks = re.split(r"(?s)({{.*?}}|{%.*?%}|{#.*?#})", text)
44
45 # Parse the tokens into a nested list of operations. Each item in the
46 # list is a tuple with an opcode, and arguments. They'll be
47 # interpreted by TempliteEngine.
48 #
49 # When parsing an action tag with nested content (if, for), the current
50 # ops list is pushed onto ops_stack, and the parsing continues in a new
51 # ops list that is part of the arguments to the if or for op.
52 ops = []
53 ops_stack = []
54 for tok in toks:
55 if tok.startswith('{{'):
56 # Expression: ('exp', expr)
57 ops.append(('exp', tok[2:-2].strip()))
58 elif tok.startswith('{#'):
59 # Comment: ignore it and move on.
60 continue
61 elif tok.startswith('{%'):
62 # Action tag: split into words and parse further.
63 words = tok[2:-2].strip().split()
64 if words[0] == 'if':
65 # If: ('if', (expr, body_ops))
66 if_ops = []
67 assert len(words) == 2
68 ops.append(('if', (words[1], if_ops)))
69 ops_stack.append(ops)
70 ops = if_ops
71 elif words[0] == 'for':
72 # For: ('for', (varname, listexpr, body_ops))
73 assert len(words) == 4 and words[2] == 'in'
74 for_ops = []
75 ops.append(('for', (words[1], words[3], for_ops)))
76 ops_stack.append(ops)
77 ops = for_ops
78 elif words[0].startswith('end'):
79 # Endsomething. Pop the ops stack
80 ops = ops_stack.pop()
81 assert ops[-1][0] == words[0][3:]
82 else:
83 raise SyntaxError("Don't understand tag %r" % words)
84 else:
85 ops.append(('lit', tok))
86
87 assert not ops_stack, "Unmatched action tag: %r" % ops_stack[-1][0]
88 self.ops = ops
89
90 def render(self, context=None):
91 """Render this template by applying it to `context`.
92
93 `context` is a dictionary of values to use in this rendering.
94
95 """
96 # Make the complete context we'll use.
97 ctx = dict(self.context)
98 if context:
99 ctx.update(context)
100
101 # Run it through an engine, and return the result.
102 engine = _TempliteEngine(ctx)
103 engine.execute(self.ops)
104 return "".join(engine.result)
105
106
107 class _TempliteEngine(object):
108 """Executes Templite objects to produce strings."""
109 def __init__(self, context):
110 self.context = context
111 self.result = []
112
113 def execute(self, ops):
114 """Execute `ops` in the engine.
115
116 Called recursively for the bodies of if's and loops.
117
118 """
119 for op, args in ops:
120 if op == 'lit':
121 self.result.append(args)
122 elif op == 'exp':
123 try:
124 self.result.append(str(self.evaluate(args)))
125 except:
126 exc_class, exc, _ = sys.exc_info()
127 new_exc = exc_class("Couldn't evaluate {{ %s }}: %s"
128 % (args, exc))
129 raise new_exc
130 elif op == 'if':
131 expr, body = args
132 if self.evaluate(expr):
133 self.execute(body)
134 elif op == 'for':
135 var, lis, body = args
136 vals = self.evaluate(lis)
137 for val in vals:
138 self.context[var] = val
139 self.execute(body)
140 else:
141 raise AssertionError("TempliteEngine doesn't grok op %r" % op)
142
143 def evaluate(self, expr):
144 """Evaluate an expression.
145
146 `expr` can have pipes and dots to indicate data access and filtering.
147
148 """
149 if "|" in expr:
150 pipes = expr.split("|")
151 value = self.evaluate(pipes[0])
152 for func in pipes[1:]:
153 value = self.evaluate(func)(value)
154 elif "." in expr:
155 dots = expr.split('.')
156 value = self.evaluate(dots[0])
157 for dot in dots[1:]:
158 try:
159 value = getattr(value, dot)
160 except AttributeError:
161 value = value[dot]
162 if hasattr(value, '__call__'):
163 value = value()
164 else:
165 value = self.context[expr]
166 return value
OLDNEW
« no previous file with comments | « third_party/coverage/summary.py ('k') | third_party/coverage/version.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698