23 class TempliteValueError(ValueError): |
21 class TempliteValueError(ValueError): |
24 """Raised when an expression won't evaluate in a template.""" |
22 """Raised when an expression won't evaluate in a template.""" |
25 pass |
23 pass |
26 |
24 |
27 |
25 |
28 class CodeBuilder(object): |
26 class CodeBuilder: |
29 """Build source code conveniently.""" |
27 """Build source code conveniently.""" |
30 |
28 |
31 def __init__(self, indent=0): |
29 def __init__(self, indent=0): |
32 self.code = [] |
30 self.code = [] |
33 self.indent_level = indent |
31 self.indent_level = indent |
69 global_namespace = {} |
67 global_namespace = {} |
70 exec(python_source, global_namespace) |
68 exec(python_source, global_namespace) |
71 return global_namespace |
69 return global_namespace |
72 |
70 |
73 |
71 |
74 class Templite(object): |
72 class Templite: |
75 """A simple template renderer, for a nano-subset of Django syntax. |
73 """A simple template renderer, for a nano-subset of Django syntax. |
76 |
74 |
77 Supported constructs are extended variable access:: |
75 Supported constructs are extended variable access:: |
78 |
76 |
79 {{var.modifier.modifier|filter|filter}} |
77 {{var.modifier.modifier|filter|filter}} |
135 code.indent() |
133 code.indent() |
136 vars_code = code.add_section() |
134 vars_code = code.add_section() |
137 code.add_line("result = []") |
135 code.add_line("result = []") |
138 code.add_line("append_result = result.append") |
136 code.add_line("append_result = result.append") |
139 code.add_line("extend_result = result.extend") |
137 code.add_line("extend_result = result.extend") |
140 if env.PY2: |
138 code.add_line("to_str = str") |
141 code.add_line("to_str = unicode") |
|
142 else: |
|
143 code.add_line("to_str = str") |
|
144 |
139 |
145 buffered = [] |
140 buffered = [] |
146 |
141 |
147 def flush_output(): |
142 def flush_output(): |
148 """Force `buffered` to the code builder.""" |
143 """Force `buffered` to the code builder.""" |
191 if len(words) != 4 or words[2] != 'in': |
186 if len(words) != 4 or words[2] != 'in': |
192 self._syntax_error("Don't understand for", token) |
187 self._syntax_error("Don't understand for", token) |
193 ops_stack.append('for') |
188 ops_stack.append('for') |
194 self._variable(words[1], self.loop_vars) |
189 self._variable(words[1], self.loop_vars) |
195 code.add_line( |
190 code.add_line( |
196 "for c_%s in %s:" % ( |
191 "for c_{} in {}:".format( |
197 words[1], |
192 words[1], |
198 self._expr_code(words[3]) |
193 self._expr_code(words[3]) |
199 ) |
194 ) |
200 ) |
195 ) |
201 code.indent() |
196 code.indent() |
231 self._syntax_error("Unmatched action tag", ops_stack[-1]) |
226 self._syntax_error("Unmatched action tag", ops_stack[-1]) |
232 |
227 |
233 flush_output() |
228 flush_output() |
234 |
229 |
235 for var_name in self.all_vars - self.loop_vars: |
230 for var_name in self.all_vars - self.loop_vars: |
236 vars_code.add_line("c_%s = context[%r]" % (var_name, var_name)) |
231 vars_code.add_line(f"c_{var_name} = context[{var_name!r}]") |
237 |
232 |
238 code.add_line('return "".join(result)') |
233 code.add_line('return "".join(result)') |
239 code.dedent() |
234 code.dedent() |
240 self._render_function = code.get_globals()['render_function'] |
235 self._render_function = code.get_globals()['render_function'] |
241 |
236 |
244 if "|" in expr: |
239 if "|" in expr: |
245 pipes = expr.split("|") |
240 pipes = expr.split("|") |
246 code = self._expr_code(pipes[0]) |
241 code = self._expr_code(pipes[0]) |
247 for func in pipes[1:]: |
242 for func in pipes[1:]: |
248 self._variable(func, self.all_vars) |
243 self._variable(func, self.all_vars) |
249 code = "c_%s(%s)" % (func, code) |
244 code = f"c_{func}({code})" |
250 elif "." in expr: |
245 elif "." in expr: |
251 dots = expr.split(".") |
246 dots = expr.split(".") |
252 code = self._expr_code(dots[0]) |
247 code = self._expr_code(dots[0]) |
253 args = ", ".join(repr(d) for d in dots[1:]) |
248 args = ", ".join(repr(d) for d in dots[1:]) |
254 code = "do_dots(%s, %s)" % (code, args) |
249 code = f"do_dots({code}, {args})" |
255 else: |
250 else: |
256 self._variable(expr, self.all_vars) |
251 self._variable(expr, self.all_vars) |
257 code = "c_%s" % expr |
252 code = "c_%s" % expr |
258 return code |
253 return code |
259 |
254 |
260 def _syntax_error(self, msg, thing): |
255 def _syntax_error(self, msg, thing): |
261 """Raise a syntax error using `msg`, and showing `thing`.""" |
256 """Raise a syntax error using `msg`, and showing `thing`.""" |
262 raise TempliteSyntaxError("%s: %r" % (msg, thing)) |
257 raise TempliteSyntaxError(f"{msg}: {thing!r}") |
263 |
258 |
264 def _variable(self, name, vars_set): |
259 def _variable(self, name, vars_set): |
265 """Track that `name` is used as a variable. |
260 """Track that `name` is used as a variable. |
266 |
261 |
267 Adds the name to `vars_set`, a set of variable names. |
262 Adds the name to `vars_set`, a set of variable names. |
291 try: |
286 try: |
292 value = getattr(value, dot) |
287 value = getattr(value, dot) |
293 except AttributeError: |
288 except AttributeError: |
294 try: |
289 try: |
295 value = value[dot] |
290 value = value[dot] |
296 except (TypeError, KeyError): |
291 except (TypeError, KeyError) as exc: |
297 raise TempliteValueError( |
292 raise TempliteValueError( |
298 "Couldn't evaluate %r.%s" % (value, dot) |
293 f"Couldn't evaluate {value!r}.{dot}" |
299 ) |
294 ) from exc |
300 if callable(value): |
295 if callable(value): |
301 value = value() |
296 value = value() |
302 return value |
297 return value |