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

Side by Side Diff: mojo/public/third_party/jinja2/compiler.py

Issue 2250183003: Make the fuchsia mojo/public repo the source of truth. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 4 years, 4 months 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
« no previous file with comments | « mojo/public/third_party/jinja2/bccache.py ('k') | mojo/public/third_party/jinja2/constants.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 # -*- coding: utf-8 -*-
2 """
3 jinja2.compiler
4 ~~~~~~~~~~~~~~~
5
6 Compiles nodes into python code.
7
8 :copyright: (c) 2010 by the Jinja Team.
9 :license: BSD, see LICENSE for more details.
10 """
11 from itertools import chain
12 from copy import deepcopy
13 from keyword import iskeyword as is_python_keyword
14 from jinja2 import nodes
15 from jinja2.nodes import EvalContext
16 from jinja2.visitor import NodeVisitor
17 from jinja2.exceptions import TemplateAssertionError
18 from jinja2.utils import Markup, concat, escape
19 from jinja2._compat import range_type, next, text_type, string_types, \
20 iteritems, NativeStringIO, imap
21
22
23 operators = {
24 'eq': '==',
25 'ne': '!=',
26 'gt': '>',
27 'gteq': '>=',
28 'lt': '<',
29 'lteq': '<=',
30 'in': 'in',
31 'notin': 'not in'
32 }
33
34 # what method to iterate over items do we want to use for dict iteration
35 # in generated code? on 2.x let's go with iteritems, on 3.x with items
36 if hasattr(dict, 'iteritems'):
37 dict_item_iter = 'iteritems'
38 else:
39 dict_item_iter = 'items'
40
41
42 # does if 0: dummy(x) get us x into the scope?
43 def unoptimize_before_dead_code():
44 x = 42
45 def f():
46 if 0: dummy(x)
47 return f
48
49 # The getattr is necessary for pypy which does not set this attribute if
50 # no closure is on the function
51 unoptimize_before_dead_code = bool(
52 getattr(unoptimize_before_dead_code(), '__closure__', None))
53
54
55 def generate(node, environment, name, filename, stream=None,
56 defer_init=False):
57 """Generate the python source for a node tree."""
58 if not isinstance(node, nodes.Template):
59 raise TypeError('Can\'t compile non template nodes')
60 generator = CodeGenerator(environment, name, filename, stream, defer_init)
61 generator.visit(node)
62 if stream is None:
63 return generator.stream.getvalue()
64
65
66 def has_safe_repr(value):
67 """Does the node have a safe representation?"""
68 if value is None or value is NotImplemented or value is Ellipsis:
69 return True
70 if isinstance(value, (bool, int, float, complex, range_type,
71 Markup) + string_types):
72 return True
73 if isinstance(value, (tuple, list, set, frozenset)):
74 for item in value:
75 if not has_safe_repr(item):
76 return False
77 return True
78 elif isinstance(value, dict):
79 for key, value in iteritems(value):
80 if not has_safe_repr(key):
81 return False
82 if not has_safe_repr(value):
83 return False
84 return True
85 return False
86
87
88 def find_undeclared(nodes, names):
89 """Check if the names passed are accessed undeclared. The return value
90 is a set of all the undeclared names from the sequence of names found.
91 """
92 visitor = UndeclaredNameVisitor(names)
93 try:
94 for node in nodes:
95 visitor.visit(node)
96 except VisitorExit:
97 pass
98 return visitor.undeclared
99
100
101 class Identifiers(object):
102 """Tracks the status of identifiers in frames."""
103
104 def __init__(self):
105 # variables that are known to be declared (probably from outer
106 # frames or because they are special for the frame)
107 self.declared = set()
108
109 # undeclared variables from outer scopes
110 self.outer_undeclared = set()
111
112 # names that are accessed without being explicitly declared by
113 # this one or any of the outer scopes. Names can appear both in
114 # declared and undeclared.
115 self.undeclared = set()
116
117 # names that are declared locally
118 self.declared_locally = set()
119
120 # names that are declared by parameters
121 self.declared_parameter = set()
122
123 def add_special(self, name):
124 """Register a special name like `loop`."""
125 self.undeclared.discard(name)
126 self.declared.add(name)
127
128 def is_declared(self, name):
129 """Check if a name is declared in this or an outer scope."""
130 if name in self.declared_locally or name in self.declared_parameter:
131 return True
132 return name in self.declared
133
134 def copy(self):
135 return deepcopy(self)
136
137
138 class Frame(object):
139 """Holds compile time information for us."""
140
141 def __init__(self, eval_ctx, parent=None):
142 self.eval_ctx = eval_ctx
143 self.identifiers = Identifiers()
144
145 # a toplevel frame is the root + soft frames such as if conditions.
146 self.toplevel = False
147
148 # the root frame is basically just the outermost frame, so no if
149 # conditions. This information is used to optimize inheritance
150 # situations.
151 self.rootlevel = False
152
153 # in some dynamic inheritance situations the compiler needs to add
154 # write tests around output statements.
155 self.require_output_check = parent and parent.require_output_check
156
157 # inside some tags we are using a buffer rather than yield statements.
158 # this for example affects {% filter %} or {% macro %}. If a frame
159 # is buffered this variable points to the name of the list used as
160 # buffer.
161 self.buffer = None
162
163 # the name of the block we're in, otherwise None.
164 self.block = parent and parent.block or None
165
166 # a set of actually assigned names
167 self.assigned_names = set()
168
169 # the parent of this frame
170 self.parent = parent
171
172 if parent is not None:
173 self.identifiers.declared.update(
174 parent.identifiers.declared |
175 parent.identifiers.declared_parameter |
176 parent.assigned_names
177 )
178 self.identifiers.outer_undeclared.update(
179 parent.identifiers.undeclared -
180 self.identifiers.declared
181 )
182 self.buffer = parent.buffer
183
184 def copy(self):
185 """Create a copy of the current one."""
186 rv = object.__new__(self.__class__)
187 rv.__dict__.update(self.__dict__)
188 rv.identifiers = object.__new__(self.identifiers.__class__)
189 rv.identifiers.__dict__.update(self.identifiers.__dict__)
190 return rv
191
192 def inspect(self, nodes):
193 """Walk the node and check for identifiers. If the scope is hard (eg:
194 enforce on a python level) overrides from outer scopes are tracked
195 differently.
196 """
197 visitor = FrameIdentifierVisitor(self.identifiers)
198 for node in nodes:
199 visitor.visit(node)
200
201 def find_shadowed(self, extra=()):
202 """Find all the shadowed names. extra is an iterable of variables
203 that may be defined with `add_special` which may occour scoped.
204 """
205 i = self.identifiers
206 return (i.declared | i.outer_undeclared) & \
207 (i.declared_locally | i.declared_parameter) | \
208 set(x for x in extra if i.is_declared(x))
209
210 def inner(self):
211 """Return an inner frame."""
212 return Frame(self.eval_ctx, self)
213
214 def soft(self):
215 """Return a soft frame. A soft frame may not be modified as
216 standalone thing as it shares the resources with the frame it
217 was created of, but it's not a rootlevel frame any longer.
218 """
219 rv = self.copy()
220 rv.rootlevel = False
221 return rv
222
223 __copy__ = copy
224
225
226 class VisitorExit(RuntimeError):
227 """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
228
229
230 class DependencyFinderVisitor(NodeVisitor):
231 """A visitor that collects filter and test calls."""
232
233 def __init__(self):
234 self.filters = set()
235 self.tests = set()
236
237 def visit_Filter(self, node):
238 self.generic_visit(node)
239 self.filters.add(node.name)
240
241 def visit_Test(self, node):
242 self.generic_visit(node)
243 self.tests.add(node.name)
244
245 def visit_Block(self, node):
246 """Stop visiting at blocks."""
247
248
249 class UndeclaredNameVisitor(NodeVisitor):
250 """A visitor that checks if a name is accessed without being
251 declared. This is different from the frame visitor as it will
252 not stop at closure frames.
253 """
254
255 def __init__(self, names):
256 self.names = set(names)
257 self.undeclared = set()
258
259 def visit_Name(self, node):
260 if node.ctx == 'load' and node.name in self.names:
261 self.undeclared.add(node.name)
262 if self.undeclared == self.names:
263 raise VisitorExit()
264 else:
265 self.names.discard(node.name)
266
267 def visit_Block(self, node):
268 """Stop visiting a blocks."""
269
270
271 class FrameIdentifierVisitor(NodeVisitor):
272 """A visitor for `Frame.inspect`."""
273
274 def __init__(self, identifiers):
275 self.identifiers = identifiers
276
277 def visit_Name(self, node):
278 """All assignments to names go through this function."""
279 if node.ctx == 'store':
280 self.identifiers.declared_locally.add(node.name)
281 elif node.ctx == 'param':
282 self.identifiers.declared_parameter.add(node.name)
283 elif node.ctx == 'load' and not \
284 self.identifiers.is_declared(node.name):
285 self.identifiers.undeclared.add(node.name)
286
287 def visit_If(self, node):
288 self.visit(node.test)
289 real_identifiers = self.identifiers
290
291 old_names = real_identifiers.declared_locally | \
292 real_identifiers.declared_parameter
293
294 def inner_visit(nodes):
295 if not nodes:
296 return set()
297 self.identifiers = real_identifiers.copy()
298 for subnode in nodes:
299 self.visit(subnode)
300 rv = self.identifiers.declared_locally - old_names
301 # we have to remember the undeclared variables of this branch
302 # because we will have to pull them.
303 real_identifiers.undeclared.update(self.identifiers.undeclared)
304 self.identifiers = real_identifiers
305 return rv
306
307 body = inner_visit(node.body)
308 else_ = inner_visit(node.else_ or ())
309
310 # the differences between the two branches are also pulled as
311 # undeclared variables
312 real_identifiers.undeclared.update(body.symmetric_difference(else_) -
313 real_identifiers.declared)
314
315 # remember those that are declared.
316 real_identifiers.declared_locally.update(body | else_)
317
318 def visit_Macro(self, node):
319 self.identifiers.declared_locally.add(node.name)
320
321 def visit_Import(self, node):
322 self.generic_visit(node)
323 self.identifiers.declared_locally.add(node.target)
324
325 def visit_FromImport(self, node):
326 self.generic_visit(node)
327 for name in node.names:
328 if isinstance(name, tuple):
329 self.identifiers.declared_locally.add(name[1])
330 else:
331 self.identifiers.declared_locally.add(name)
332
333 def visit_Assign(self, node):
334 """Visit assignments in the correct order."""
335 self.visit(node.node)
336 self.visit(node.target)
337
338 def visit_For(self, node):
339 """Visiting stops at for blocks. However the block sequence
340 is visited as part of the outer scope.
341 """
342 self.visit(node.iter)
343
344 def visit_CallBlock(self, node):
345 self.visit(node.call)
346
347 def visit_FilterBlock(self, node):
348 self.visit(node.filter)
349
350 def visit_Scope(self, node):
351 """Stop visiting at scopes."""
352
353 def visit_Block(self, node):
354 """Stop visiting at blocks."""
355
356
357 class CompilerExit(Exception):
358 """Raised if the compiler encountered a situation where it just
359 doesn't make sense to further process the code. Any block that
360 raises such an exception is not further processed.
361 """
362
363
364 class CodeGenerator(NodeVisitor):
365
366 def __init__(self, environment, name, filename, stream=None,
367 defer_init=False):
368 if stream is None:
369 stream = NativeStringIO()
370 self.environment = environment
371 self.name = name
372 self.filename = filename
373 self.stream = stream
374 self.created_block_context = False
375 self.defer_init = defer_init
376
377 # aliases for imports
378 self.import_aliases = {}
379
380 # a registry for all blocks. Because blocks are moved out
381 # into the global python scope they are registered here
382 self.blocks = {}
383
384 # the number of extends statements so far
385 self.extends_so_far = 0
386
387 # some templates have a rootlevel extends. In this case we
388 # can safely assume that we're a child template and do some
389 # more optimizations.
390 self.has_known_extends = False
391
392 # the current line number
393 self.code_lineno = 1
394
395 # registry of all filters and tests (global, not block local)
396 self.tests = {}
397 self.filters = {}
398
399 # the debug information
400 self.debug_info = []
401 self._write_debug_info = None
402
403 # the number of new lines before the next write()
404 self._new_lines = 0
405
406 # the line number of the last written statement
407 self._last_line = 0
408
409 # true if nothing was written so far.
410 self._first_write = True
411
412 # used by the `temporary_identifier` method to get new
413 # unique, temporary identifier
414 self._last_identifier = 0
415
416 # the current indentation
417 self._indentation = 0
418
419 # -- Various compilation helpers
420
421 def fail(self, msg, lineno):
422 """Fail with a :exc:`TemplateAssertionError`."""
423 raise TemplateAssertionError(msg, lineno, self.name, self.filename)
424
425 def temporary_identifier(self):
426 """Get a new unique identifier."""
427 self._last_identifier += 1
428 return 't_%d' % self._last_identifier
429
430 def buffer(self, frame):
431 """Enable buffering for the frame from that point onwards."""
432 frame.buffer = self.temporary_identifier()
433 self.writeline('%s = []' % frame.buffer)
434
435 def return_buffer_contents(self, frame):
436 """Return the buffer contents of the frame."""
437 if frame.eval_ctx.volatile:
438 self.writeline('if context.eval_ctx.autoescape:')
439 self.indent()
440 self.writeline('return Markup(concat(%s))' % frame.buffer)
441 self.outdent()
442 self.writeline('else:')
443 self.indent()
444 self.writeline('return concat(%s)' % frame.buffer)
445 self.outdent()
446 elif frame.eval_ctx.autoescape:
447 self.writeline('return Markup(concat(%s))' % frame.buffer)
448 else:
449 self.writeline('return concat(%s)' % frame.buffer)
450
451 def indent(self):
452 """Indent by one."""
453 self._indentation += 1
454
455 def outdent(self, step=1):
456 """Outdent by step."""
457 self._indentation -= step
458
459 def start_write(self, frame, node=None):
460 """Yield or write into the frame buffer."""
461 if frame.buffer is None:
462 self.writeline('yield ', node)
463 else:
464 self.writeline('%s.append(' % frame.buffer, node)
465
466 def end_write(self, frame):
467 """End the writing process started by `start_write`."""
468 if frame.buffer is not None:
469 self.write(')')
470
471 def simple_write(self, s, frame, node=None):
472 """Simple shortcut for start_write + write + end_write."""
473 self.start_write(frame, node)
474 self.write(s)
475 self.end_write(frame)
476
477 def blockvisit(self, nodes, frame):
478 """Visit a list of nodes as block in a frame. If the current frame
479 is no buffer a dummy ``if 0: yield None`` is written automatically
480 unless the force_generator parameter is set to False.
481 """
482 if frame.buffer is None:
483 self.writeline('if 0: yield None')
484 else:
485 self.writeline('pass')
486 try:
487 for node in nodes:
488 self.visit(node, frame)
489 except CompilerExit:
490 pass
491
492 def write(self, x):
493 """Write a string into the output stream."""
494 if self._new_lines:
495 if not self._first_write:
496 self.stream.write('\n' * self._new_lines)
497 self.code_lineno += self._new_lines
498 if self._write_debug_info is not None:
499 self.debug_info.append((self._write_debug_info,
500 self.code_lineno))
501 self._write_debug_info = None
502 self._first_write = False
503 self.stream.write(' ' * self._indentation)
504 self._new_lines = 0
505 self.stream.write(x)
506
507 def writeline(self, x, node=None, extra=0):
508 """Combination of newline and write."""
509 self.newline(node, extra)
510 self.write(x)
511
512 def newline(self, node=None, extra=0):
513 """Add one or more newlines before the next write."""
514 self._new_lines = max(self._new_lines, 1 + extra)
515 if node is not None and node.lineno != self._last_line:
516 self._write_debug_info = node.lineno
517 self._last_line = node.lineno
518
519 def signature(self, node, frame, extra_kwargs=None):
520 """Writes a function call to the stream for the current node.
521 A leading comma is added automatically. The extra keyword
522 arguments may not include python keywords otherwise a syntax
523 error could occour. The extra keyword arguments should be given
524 as python dict.
525 """
526 # if any of the given keyword arguments is a python keyword
527 # we have to make sure that no invalid call is created.
528 kwarg_workaround = False
529 for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()):
530 if is_python_keyword(kwarg):
531 kwarg_workaround = True
532 break
533
534 for arg in node.args:
535 self.write(', ')
536 self.visit(arg, frame)
537
538 if not kwarg_workaround:
539 for kwarg in node.kwargs:
540 self.write(', ')
541 self.visit(kwarg, frame)
542 if extra_kwargs is not None:
543 for key, value in iteritems(extra_kwargs):
544 self.write(', %s=%s' % (key, value))
545 if node.dyn_args:
546 self.write(', *')
547 self.visit(node.dyn_args, frame)
548
549 if kwarg_workaround:
550 if node.dyn_kwargs is not None:
551 self.write(', **dict({')
552 else:
553 self.write(', **{')
554 for kwarg in node.kwargs:
555 self.write('%r: ' % kwarg.key)
556 self.visit(kwarg.value, frame)
557 self.write(', ')
558 if extra_kwargs is not None:
559 for key, value in iteritems(extra_kwargs):
560 self.write('%r: %s, ' % (key, value))
561 if node.dyn_kwargs is not None:
562 self.write('}, **')
563 self.visit(node.dyn_kwargs, frame)
564 self.write(')')
565 else:
566 self.write('}')
567
568 elif node.dyn_kwargs is not None:
569 self.write(', **')
570 self.visit(node.dyn_kwargs, frame)
571
572 def pull_locals(self, frame):
573 """Pull all the references identifiers into the local scope."""
574 for name in frame.identifiers.undeclared:
575 self.writeline('l_%s = context.resolve(%r)' % (name, name))
576
577 def pull_dependencies(self, nodes):
578 """Pull all the dependencies."""
579 visitor = DependencyFinderVisitor()
580 for node in nodes:
581 visitor.visit(node)
582 for dependency in 'filters', 'tests':
583 mapping = getattr(self, dependency)
584 for name in getattr(visitor, dependency):
585 if name not in mapping:
586 mapping[name] = self.temporary_identifier()
587 self.writeline('%s = environment.%s[%r]' %
588 (mapping[name], dependency, name))
589
590 def unoptimize_scope(self, frame):
591 """Disable Python optimizations for the frame."""
592 # XXX: this is not that nice but it has no real overhead. It
593 # mainly works because python finds the locals before dead code
594 # is removed. If that breaks we have to add a dummy function
595 # that just accepts the arguments and does nothing.
596 if frame.identifiers.declared:
597 self.writeline('%sdummy(%s)' % (
598 unoptimize_before_dead_code and 'if 0: ' or '',
599 ', '.join('l_' + name for name in frame.identifiers.declared)
600 ))
601
602 def push_scope(self, frame, extra_vars=()):
603 """This function returns all the shadowed variables in a dict
604 in the form name: alias and will write the required assignments
605 into the current scope. No indentation takes place.
606
607 This also predefines locally declared variables from the loop
608 body because under some circumstances it may be the case that
609
610 `extra_vars` is passed to `Frame.find_shadowed`.
611 """
612 aliases = {}
613 for name in frame.find_shadowed(extra_vars):
614 aliases[name] = ident = self.temporary_identifier()
615 self.writeline('%s = l_%s' % (ident, name))
616 to_declare = set()
617 for name in frame.identifiers.declared_locally:
618 if name not in aliases:
619 to_declare.add('l_' + name)
620 if to_declare:
621 self.writeline(' = '.join(to_declare) + ' = missing')
622 return aliases
623
624 def pop_scope(self, aliases, frame):
625 """Restore all aliases and delete unused variables."""
626 for name, alias in iteritems(aliases):
627 self.writeline('l_%s = %s' % (name, alias))
628 to_delete = set()
629 for name in frame.identifiers.declared_locally:
630 if name not in aliases:
631 to_delete.add('l_' + name)
632 if to_delete:
633 # we cannot use the del statement here because enclosed
634 # scopes can trigger a SyntaxError:
635 # a = 42; b = lambda: a; del a
636 self.writeline(' = '.join(to_delete) + ' = missing')
637
638 def function_scoping(self, node, frame, children=None,
639 find_special=True):
640 """In Jinja a few statements require the help of anonymous
641 functions. Those are currently macros and call blocks and in
642 the future also recursive loops. As there is currently
643 technical limitation that doesn't allow reading and writing a
644 variable in a scope where the initial value is coming from an
645 outer scope, this function tries to fall back with a common
646 error message. Additionally the frame passed is modified so
647 that the argumetns are collected and callers are looked up.
648
649 This will return the modified frame.
650 """
651 # we have to iterate twice over it, make sure that works
652 if children is None:
653 children = node.iter_child_nodes()
654 children = list(children)
655 func_frame = frame.inner()
656 func_frame.inspect(children)
657
658 # variables that are undeclared (accessed before declaration) and
659 # declared locally *and* part of an outside scope raise a template
660 # assertion error. Reason: we can't generate reasonable code from
661 # it without aliasing all the variables.
662 # this could be fixed in Python 3 where we have the nonlocal
663 # keyword or if we switch to bytecode generation
664 overridden_closure_vars = (
665 func_frame.identifiers.undeclared &
666 func_frame.identifiers.declared &
667 (func_frame.identifiers.declared_locally |
668 func_frame.identifiers.declared_parameter)
669 )
670 if overridden_closure_vars:
671 self.fail('It\'s not possible to set and access variables '
672 'derived from an outer scope! (affects: %s)' %
673 ', '.join(sorted(overridden_closure_vars)), node.lineno)
674
675 # remove variables from a closure from the frame's undeclared
676 # identifiers.
677 func_frame.identifiers.undeclared -= (
678 func_frame.identifiers.undeclared &
679 func_frame.identifiers.declared
680 )
681
682 # no special variables for this scope, abort early
683 if not find_special:
684 return func_frame
685
686 func_frame.accesses_kwargs = False
687 func_frame.accesses_varargs = False
688 func_frame.accesses_caller = False
689 func_frame.arguments = args = ['l_' + x.name for x in node.args]
690
691 undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs'))
692
693 if 'caller' in undeclared:
694 func_frame.accesses_caller = True
695 func_frame.identifiers.add_special('caller')
696 args.append('l_caller')
697 if 'kwargs' in undeclared:
698 func_frame.accesses_kwargs = True
699 func_frame.identifiers.add_special('kwargs')
700 args.append('l_kwargs')
701 if 'varargs' in undeclared:
702 func_frame.accesses_varargs = True
703 func_frame.identifiers.add_special('varargs')
704 args.append('l_varargs')
705 return func_frame
706
707 def macro_body(self, node, frame, children=None):
708 """Dump the function def of a macro or call block."""
709 frame = self.function_scoping(node, frame, children)
710 # macros are delayed, they never require output checks
711 frame.require_output_check = False
712 args = frame.arguments
713 # XXX: this is an ugly fix for the loop nesting bug
714 # (tests.test_old_bugs.test_loop_call_bug). This works around
715 # a identifier nesting problem we have in general. It's just more
716 # likely to happen in loops which is why we work around it. The
717 # real solution would be "nonlocal" all the identifiers that are
718 # leaking into a new python frame and might be used both unassigned
719 # and assigned.
720 if 'loop' in frame.identifiers.declared:
721 args = args + ['l_loop=l_loop']
722 self.writeline('def macro(%s):' % ', '.join(args), node)
723 self.indent()
724 self.buffer(frame)
725 self.pull_locals(frame)
726 self.blockvisit(node.body, frame)
727 self.return_buffer_contents(frame)
728 self.outdent()
729 return frame
730
731 def macro_def(self, node, frame):
732 """Dump the macro definition for the def created by macro_body."""
733 arg_tuple = ', '.join(repr(x.name) for x in node.args)
734 name = getattr(node, 'name', None)
735 if len(node.args) == 1:
736 arg_tuple += ','
737 self.write('Macro(environment, macro, %r, (%s), (' %
738 (name, arg_tuple))
739 for arg in node.defaults:
740 self.visit(arg, frame)
741 self.write(', ')
742 self.write('), %r, %r, %r)' % (
743 bool(frame.accesses_kwargs),
744 bool(frame.accesses_varargs),
745 bool(frame.accesses_caller)
746 ))
747
748 def position(self, node):
749 """Return a human readable position for the node."""
750 rv = 'line %d' % node.lineno
751 if self.name is not None:
752 rv += ' in ' + repr(self.name)
753 return rv
754
755 # -- Statement Visitors
756
757 def visit_Template(self, node, frame=None):
758 assert frame is None, 'no root frame allowed'
759 eval_ctx = EvalContext(self.environment, self.name)
760
761 from jinja2.runtime import __all__ as exported
762 self.writeline('from __future__ import division')
763 self.writeline('from jinja2.runtime import ' + ', '.join(exported))
764 if not unoptimize_before_dead_code:
765 self.writeline('dummy = lambda *x: None')
766
767 # if we want a deferred initialization we cannot move the
768 # environment into a local name
769 envenv = not self.defer_init and ', environment=environment' or ''
770
771 # do we have an extends tag at all? If not, we can save some
772 # overhead by just not processing any inheritance code.
773 have_extends = node.find(nodes.Extends) is not None
774
775 # find all blocks
776 for block in node.find_all(nodes.Block):
777 if block.name in self.blocks:
778 self.fail('block %r defined twice' % block.name, block.lineno)
779 self.blocks[block.name] = block
780
781 # find all imports and import them
782 for import_ in node.find_all(nodes.ImportedName):
783 if import_.importname not in self.import_aliases:
784 imp = import_.importname
785 self.import_aliases[imp] = alias = self.temporary_identifier()
786 if '.' in imp:
787 module, obj = imp.rsplit('.', 1)
788 self.writeline('from %s import %s as %s' %
789 (module, obj, alias))
790 else:
791 self.writeline('import %s as %s' % (imp, alias))
792
793 # add the load name
794 self.writeline('name = %r' % self.name)
795
796 # generate the root render function.
797 self.writeline('def root(context%s):' % envenv, extra=1)
798
799 # process the root
800 frame = Frame(eval_ctx)
801 frame.inspect(node.body)
802 frame.toplevel = frame.rootlevel = True
803 frame.require_output_check = have_extends and not self.has_known_extends
804 self.indent()
805 if have_extends:
806 self.writeline('parent_template = None')
807 if 'self' in find_undeclared(node.body, ('self',)):
808 frame.identifiers.add_special('self')
809 self.writeline('l_self = TemplateReference(context)')
810 self.pull_locals(frame)
811 self.pull_dependencies(node.body)
812 self.blockvisit(node.body, frame)
813 self.outdent()
814
815 # make sure that the parent root is called.
816 if have_extends:
817 if not self.has_known_extends:
818 self.indent()
819 self.writeline('if parent_template is not None:')
820 self.indent()
821 self.writeline('for event in parent_template.'
822 'root_render_func(context):')
823 self.indent()
824 self.writeline('yield event')
825 self.outdent(2 + (not self.has_known_extends))
826
827 # at this point we now have the blocks collected and can visit them too.
828 for name, block in iteritems(self.blocks):
829 block_frame = Frame(eval_ctx)
830 block_frame.inspect(block.body)
831 block_frame.block = name
832 self.writeline('def block_%s(context%s):' % (name, envenv),
833 block, 1)
834 self.indent()
835 undeclared = find_undeclared(block.body, ('self', 'super'))
836 if 'self' in undeclared:
837 block_frame.identifiers.add_special('self')
838 self.writeline('l_self = TemplateReference(context)')
839 if 'super' in undeclared:
840 block_frame.identifiers.add_special('super')
841 self.writeline('l_super = context.super(%r, '
842 'block_%s)' % (name, name))
843 self.pull_locals(block_frame)
844 self.pull_dependencies(block.body)
845 self.blockvisit(block.body, block_frame)
846 self.outdent()
847
848 self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
849 for x in self.blocks),
850 extra=1)
851
852 # add a function that returns the debug info
853 self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x
854 in self.debug_info))
855
856 def visit_Block(self, node, frame):
857 """Call a block and register it for the template."""
858 level = 1
859 if frame.toplevel:
860 # if we know that we are a child template, there is no need to
861 # check if we are one
862 if self.has_known_extends:
863 return
864 if self.extends_so_far > 0:
865 self.writeline('if parent_template is None:')
866 self.indent()
867 level += 1
868 context = node.scoped and 'context.derived(locals())' or 'context'
869 self.writeline('for event in context.blocks[%r][0](%s):' % (
870 node.name, context), node)
871 self.indent()
872 self.simple_write('event', frame)
873 self.outdent(level)
874
875 def visit_Extends(self, node, frame):
876 """Calls the extender."""
877 if not frame.toplevel:
878 self.fail('cannot use extend from a non top-level scope',
879 node.lineno)
880
881 # if the number of extends statements in general is zero so
882 # far, we don't have to add a check if something extended
883 # the template before this one.
884 if self.extends_so_far > 0:
885
886 # if we have a known extends we just add a template runtime
887 # error into the generated code. We could catch that at compile
888 # time too, but i welcome it not to confuse users by throwing the
889 # same error at different times just "because we can".
890 if not self.has_known_extends:
891 self.writeline('if parent_template is not None:')
892 self.indent()
893 self.writeline('raise TemplateRuntimeError(%r)' %
894 'extended multiple times')
895
896 # if we have a known extends already we don't need that code here
897 # as we know that the template execution will end here.
898 if self.has_known_extends:
899 raise CompilerExit()
900 else:
901 self.outdent()
902
903 self.writeline('parent_template = environment.get_template(', node)
904 self.visit(node.template, frame)
905 self.write(', %r)' % self.name)
906 self.writeline('for name, parent_block in parent_template.'
907 'blocks.%s():' % dict_item_iter)
908 self.indent()
909 self.writeline('context.blocks.setdefault(name, []).'
910 'append(parent_block)')
911 self.outdent()
912
913 # if this extends statement was in the root level we can take
914 # advantage of that information and simplify the generated code
915 # in the top level from this point onwards
916 if frame.rootlevel:
917 self.has_known_extends = True
918
919 # and now we have one more
920 self.extends_so_far += 1
921
922 def visit_Include(self, node, frame):
923 """Handles includes."""
924 if node.with_context:
925 self.unoptimize_scope(frame)
926 if node.ignore_missing:
927 self.writeline('try:')
928 self.indent()
929
930 func_name = 'get_or_select_template'
931 if isinstance(node.template, nodes.Const):
932 if isinstance(node.template.value, string_types):
933 func_name = 'get_template'
934 elif isinstance(node.template.value, (tuple, list)):
935 func_name = 'select_template'
936 elif isinstance(node.template, (nodes.Tuple, nodes.List)):
937 func_name = 'select_template'
938
939 self.writeline('template = environment.%s(' % func_name, node)
940 self.visit(node.template, frame)
941 self.write(', %r)' % self.name)
942 if node.ignore_missing:
943 self.outdent()
944 self.writeline('except TemplateNotFound:')
945 self.indent()
946 self.writeline('pass')
947 self.outdent()
948 self.writeline('else:')
949 self.indent()
950
951 if node.with_context:
952 self.writeline('for event in template.root_render_func('
953 'template.new_context(context.parent, True, '
954 'locals())):')
955 else:
956 self.writeline('for event in template.module._body_stream:')
957
958 self.indent()
959 self.simple_write('event', frame)
960 self.outdent()
961
962 if node.ignore_missing:
963 self.outdent()
964
965 def visit_Import(self, node, frame):
966 """Visit regular imports."""
967 if node.with_context:
968 self.unoptimize_scope(frame)
969 self.writeline('l_%s = ' % node.target, node)
970 if frame.toplevel:
971 self.write('context.vars[%r] = ' % node.target)
972 self.write('environment.get_template(')
973 self.visit(node.template, frame)
974 self.write(', %r).' % self.name)
975 if node.with_context:
976 self.write('make_module(context.parent, True, locals())')
977 else:
978 self.write('module')
979 if frame.toplevel and not node.target.startswith('_'):
980 self.writeline('context.exported_vars.discard(%r)' % node.target)
981 frame.assigned_names.add(node.target)
982
983 def visit_FromImport(self, node, frame):
984 """Visit named imports."""
985 self.newline(node)
986 self.write('included_template = environment.get_template(')
987 self.visit(node.template, frame)
988 self.write(', %r).' % self.name)
989 if node.with_context:
990 self.write('make_module(context.parent, True)')
991 else:
992 self.write('module')
993
994 var_names = []
995 discarded_names = []
996 for name in node.names:
997 if isinstance(name, tuple):
998 name, alias = name
999 else:
1000 alias = name
1001 self.writeline('l_%s = getattr(included_template, '
1002 '%r, missing)' % (alias, name))
1003 self.writeline('if l_%s is missing:' % alias)
1004 self.indent()
1005 self.writeline('l_%s = environment.undefined(%r %% '
1006 'included_template.__name__, '
1007 'name=%r)' %
1008 (alias, 'the template %%r (imported on %s) does '
1009 'not export the requested name %s' % (
1010 self.position(node),
1011 repr(name)
1012 ), name))
1013 self.outdent()
1014 if frame.toplevel:
1015 var_names.append(alias)
1016 if not alias.startswith('_'):
1017 discarded_names.append(alias)
1018 frame.assigned_names.add(alias)
1019
1020 if var_names:
1021 if len(var_names) == 1:
1022 name = var_names[0]
1023 self.writeline('context.vars[%r] = l_%s' % (name, name))
1024 else:
1025 self.writeline('context.vars.update({%s})' % ', '.join(
1026 '%r: l_%s' % (name, name) for name in var_names
1027 ))
1028 if discarded_names:
1029 if len(discarded_names) == 1:
1030 self.writeline('context.exported_vars.discard(%r)' %
1031 discarded_names[0])
1032 else:
1033 self.writeline('context.exported_vars.difference_'
1034 'update((%s))' % ', '.join(imap(repr, discarded_n ames)))
1035
1036 def visit_For(self, node, frame):
1037 # when calculating the nodes for the inner frame we have to exclude
1038 # the iterator contents from it
1039 children = node.iter_child_nodes(exclude=('iter',))
1040 if node.recursive:
1041 loop_frame = self.function_scoping(node, frame, children,
1042 find_special=False)
1043 else:
1044 loop_frame = frame.inner()
1045 loop_frame.inspect(children)
1046
1047 # try to figure out if we have an extended loop. An extended loop
1048 # is necessary if the loop is in recursive mode if the special loop
1049 # variable is accessed in the body.
1050 extended_loop = node.recursive or 'loop' in \
1051 find_undeclared(node.iter_child_nodes(
1052 only=('body',)), ('loop',))
1053
1054 # if we don't have an recursive loop we have to find the shadowed
1055 # variables at that point. Because loops can be nested but the loop
1056 # variable is a special one we have to enforce aliasing for it.
1057 if not node.recursive:
1058 aliases = self.push_scope(loop_frame, ('loop',))
1059
1060 # otherwise we set up a buffer and add a function def
1061 else:
1062 self.writeline('def loop(reciter, loop_render_func, depth=0):', node )
1063 self.indent()
1064 self.buffer(loop_frame)
1065 aliases = {}
1066
1067 # make sure the loop variable is a special one and raise a template
1068 # assertion error if a loop tries to write to loop
1069 if extended_loop:
1070 self.writeline('l_loop = missing')
1071 loop_frame.identifiers.add_special('loop')
1072 for name in node.find_all(nodes.Name):
1073 if name.ctx == 'store' and name.name == 'loop':
1074 self.fail('Can\'t assign to special loop variable '
1075 'in for-loop target', name.lineno)
1076
1077 self.pull_locals(loop_frame)
1078 if node.else_:
1079 iteration_indicator = self.temporary_identifier()
1080 self.writeline('%s = 1' % iteration_indicator)
1081
1082 # Create a fake parent loop if the else or test section of a
1083 # loop is accessing the special loop variable and no parent loop
1084 # exists.
1085 if 'loop' not in aliases and 'loop' in find_undeclared(
1086 node.iter_child_nodes(only=('else_', 'test')), ('loop',)):
1087 self.writeline("l_loop = environment.undefined(%r, name='loop')" %
1088 ("'loop' is undefined. the filter section of a loop as well "
1089 "as the else block don't have access to the special 'loop'"
1090 " variable of the current loop. Because there is no parent "
1091 "loop it's undefined. Happened in loop on %s" %
1092 self.position(node)))
1093
1094 self.writeline('for ', node)
1095 self.visit(node.target, loop_frame)
1096 self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
1097
1098 # if we have an extened loop and a node test, we filter in the
1099 # "outer frame".
1100 if extended_loop and node.test is not None:
1101 self.write('(')
1102 self.visit(node.target, loop_frame)
1103 self.write(' for ')
1104 self.visit(node.target, loop_frame)
1105 self.write(' in ')
1106 if node.recursive:
1107 self.write('reciter')
1108 else:
1109 self.visit(node.iter, loop_frame)
1110 self.write(' if (')
1111 test_frame = loop_frame.copy()
1112 self.visit(node.test, test_frame)
1113 self.write('))')
1114
1115 elif node.recursive:
1116 self.write('reciter')
1117 else:
1118 self.visit(node.iter, loop_frame)
1119
1120 if node.recursive:
1121 self.write(', loop_render_func, depth):')
1122 else:
1123 self.write(extended_loop and '):' or ':')
1124
1125 # tests in not extended loops become a continue
1126 if not extended_loop and node.test is not None:
1127 self.indent()
1128 self.writeline('if not ')
1129 self.visit(node.test, loop_frame)
1130 self.write(':')
1131 self.indent()
1132 self.writeline('continue')
1133 self.outdent(2)
1134
1135 self.indent()
1136 self.blockvisit(node.body, loop_frame)
1137 if node.else_:
1138 self.writeline('%s = 0' % iteration_indicator)
1139 self.outdent()
1140
1141 if node.else_:
1142 self.writeline('if %s:' % iteration_indicator)
1143 self.indent()
1144 self.blockvisit(node.else_, loop_frame)
1145 self.outdent()
1146
1147 # reset the aliases if there are any.
1148 if not node.recursive:
1149 self.pop_scope(aliases, loop_frame)
1150
1151 # if the node was recursive we have to return the buffer contents
1152 # and start the iteration code
1153 if node.recursive:
1154 self.return_buffer_contents(loop_frame)
1155 self.outdent()
1156 self.start_write(frame, node)
1157 self.write('loop(')
1158 self.visit(node.iter, frame)
1159 self.write(', loop)')
1160 self.end_write(frame)
1161
1162 def visit_If(self, node, frame):
1163 if_frame = frame.soft()
1164 self.writeline('if ', node)
1165 self.visit(node.test, if_frame)
1166 self.write(':')
1167 self.indent()
1168 self.blockvisit(node.body, if_frame)
1169 self.outdent()
1170 if node.else_:
1171 self.writeline('else:')
1172 self.indent()
1173 self.blockvisit(node.else_, if_frame)
1174 self.outdent()
1175
1176 def visit_Macro(self, node, frame):
1177 macro_frame = self.macro_body(node, frame)
1178 self.newline()
1179 if frame.toplevel:
1180 if not node.name.startswith('_'):
1181 self.write('context.exported_vars.add(%r)' % node.name)
1182 self.writeline('context.vars[%r] = ' % node.name)
1183 self.write('l_%s = ' % node.name)
1184 self.macro_def(node, macro_frame)
1185 frame.assigned_names.add(node.name)
1186
1187 def visit_CallBlock(self, node, frame):
1188 children = node.iter_child_nodes(exclude=('call',))
1189 call_frame = self.macro_body(node, frame, children)
1190 self.writeline('caller = ')
1191 self.macro_def(node, call_frame)
1192 self.start_write(frame, node)
1193 self.visit_Call(node.call, call_frame, forward_caller=True)
1194 self.end_write(frame)
1195
1196 def visit_FilterBlock(self, node, frame):
1197 filter_frame = frame.inner()
1198 filter_frame.inspect(node.iter_child_nodes())
1199 aliases = self.push_scope(filter_frame)
1200 self.pull_locals(filter_frame)
1201 self.buffer(filter_frame)
1202 self.blockvisit(node.body, filter_frame)
1203 self.start_write(frame, node)
1204 self.visit_Filter(node.filter, filter_frame)
1205 self.end_write(frame)
1206 self.pop_scope(aliases, filter_frame)
1207
1208 def visit_ExprStmt(self, node, frame):
1209 self.newline(node)
1210 self.visit(node.node, frame)
1211
1212 def visit_Output(self, node, frame):
1213 # if we have a known extends statement, we don't output anything
1214 # if we are in a require_output_check section
1215 if self.has_known_extends and frame.require_output_check:
1216 return
1217
1218 if self.environment.finalize:
1219 finalize = lambda x: text_type(self.environment.finalize(x))
1220 else:
1221 finalize = text_type
1222
1223 # if we are inside a frame that requires output checking, we do so
1224 outdent_later = False
1225 if frame.require_output_check:
1226 self.writeline('if parent_template is None:')
1227 self.indent()
1228 outdent_later = True
1229
1230 # try to evaluate as many chunks as possible into a static
1231 # string at compile time.
1232 body = []
1233 for child in node.nodes:
1234 try:
1235 const = child.as_const(frame.eval_ctx)
1236 except nodes.Impossible:
1237 body.append(child)
1238 continue
1239 # the frame can't be volatile here, becaus otherwise the
1240 # as_const() function would raise an Impossible exception
1241 # at that point.
1242 try:
1243 if frame.eval_ctx.autoescape:
1244 if hasattr(const, '__html__'):
1245 const = const.__html__()
1246 else:
1247 const = escape(const)
1248 const = finalize(const)
1249 except Exception:
1250 # if something goes wrong here we evaluate the node
1251 # at runtime for easier debugging
1252 body.append(child)
1253 continue
1254 if body and isinstance(body[-1], list):
1255 body[-1].append(const)
1256 else:
1257 body.append([const])
1258
1259 # if we have less than 3 nodes or a buffer we yield or extend/append
1260 if len(body) < 3 or frame.buffer is not None:
1261 if frame.buffer is not None:
1262 # for one item we append, for more we extend
1263 if len(body) == 1:
1264 self.writeline('%s.append(' % frame.buffer)
1265 else:
1266 self.writeline('%s.extend((' % frame.buffer)
1267 self.indent()
1268 for item in body:
1269 if isinstance(item, list):
1270 val = repr(concat(item))
1271 if frame.buffer is None:
1272 self.writeline('yield ' + val)
1273 else:
1274 self.writeline(val + ', ')
1275 else:
1276 if frame.buffer is None:
1277 self.writeline('yield ', item)
1278 else:
1279 self.newline(item)
1280 close = 1
1281 if frame.eval_ctx.volatile:
1282 self.write('(context.eval_ctx.autoescape and'
1283 ' escape or to_string)(')
1284 elif frame.eval_ctx.autoescape:
1285 self.write('escape(')
1286 else:
1287 self.write('to_string(')
1288 if self.environment.finalize is not None:
1289 self.write('environment.finalize(')
1290 close += 1
1291 self.visit(item, frame)
1292 self.write(')' * close)
1293 if frame.buffer is not None:
1294 self.write(', ')
1295 if frame.buffer is not None:
1296 # close the open parentheses
1297 self.outdent()
1298 self.writeline(len(body) == 1 and ')' or '))')
1299
1300 # otherwise we create a format string as this is faster in that case
1301 else:
1302 format = []
1303 arguments = []
1304 for item in body:
1305 if isinstance(item, list):
1306 format.append(concat(item).replace('%', '%%'))
1307 else:
1308 format.append('%s')
1309 arguments.append(item)
1310 self.writeline('yield ')
1311 self.write(repr(concat(format)) + ' % (')
1312 idx = -1
1313 self.indent()
1314 for argument in arguments:
1315 self.newline(argument)
1316 close = 0
1317 if frame.eval_ctx.volatile:
1318 self.write('(context.eval_ctx.autoescape and'
1319 ' escape or to_string)(')
1320 close += 1
1321 elif frame.eval_ctx.autoescape:
1322 self.write('escape(')
1323 close += 1
1324 if self.environment.finalize is not None:
1325 self.write('environment.finalize(')
1326 close += 1
1327 self.visit(argument, frame)
1328 self.write(')' * close + ', ')
1329 self.outdent()
1330 self.writeline(')')
1331
1332 if outdent_later:
1333 self.outdent()
1334
1335 def visit_Assign(self, node, frame):
1336 self.newline(node)
1337 # toplevel assignments however go into the local namespace and
1338 # the current template's context. We create a copy of the frame
1339 # here and add a set so that the Name visitor can add the assigned
1340 # names here.
1341 if frame.toplevel:
1342 assignment_frame = frame.copy()
1343 assignment_frame.toplevel_assignments = set()
1344 else:
1345 assignment_frame = frame
1346 self.visit(node.target, assignment_frame)
1347 self.write(' = ')
1348 self.visit(node.node, frame)
1349
1350 # make sure toplevel assignments are added to the context.
1351 if frame.toplevel:
1352 public_names = [x for x in assignment_frame.toplevel_assignments
1353 if not x.startswith('_')]
1354 if len(assignment_frame.toplevel_assignments) == 1:
1355 name = next(iter(assignment_frame.toplevel_assignments))
1356 self.writeline('context.vars[%r] = l_%s' % (name, name))
1357 else:
1358 self.writeline('context.vars.update({')
1359 for idx, name in enumerate(assignment_frame.toplevel_assignments ):
1360 if idx:
1361 self.write(', ')
1362 self.write('%r: l_%s' % (name, name))
1363 self.write('})')
1364 if public_names:
1365 if len(public_names) == 1:
1366 self.writeline('context.exported_vars.add(%r)' %
1367 public_names[0])
1368 else:
1369 self.writeline('context.exported_vars.update((%s))' %
1370 ', '.join(imap(repr, public_names)))
1371
1372 # -- Expression Visitors
1373
1374 def visit_Name(self, node, frame):
1375 if node.ctx == 'store' and frame.toplevel:
1376 frame.toplevel_assignments.add(node.name)
1377 self.write('l_' + node.name)
1378 frame.assigned_names.add(node.name)
1379
1380 def visit_Const(self, node, frame):
1381 val = node.value
1382 if isinstance(val, float):
1383 self.write(str(val))
1384 else:
1385 self.write(repr(val))
1386
1387 def visit_TemplateData(self, node, frame):
1388 try:
1389 self.write(repr(node.as_const(frame.eval_ctx)))
1390 except nodes.Impossible:
1391 self.write('(context.eval_ctx.autoescape and Markup or identity)(%r) '
1392 % node.data)
1393
1394 def visit_Tuple(self, node, frame):
1395 self.write('(')
1396 idx = -1
1397 for idx, item in enumerate(node.items):
1398 if idx:
1399 self.write(', ')
1400 self.visit(item, frame)
1401 self.write(idx == 0 and ',)' or ')')
1402
1403 def visit_List(self, node, frame):
1404 self.write('[')
1405 for idx, item in enumerate(node.items):
1406 if idx:
1407 self.write(', ')
1408 self.visit(item, frame)
1409 self.write(']')
1410
1411 def visit_Dict(self, node, frame):
1412 self.write('{')
1413 for idx, item in enumerate(node.items):
1414 if idx:
1415 self.write(', ')
1416 self.visit(item.key, frame)
1417 self.write(': ')
1418 self.visit(item.value, frame)
1419 self.write('}')
1420
1421 def binop(operator, interceptable=True):
1422 def visitor(self, node, frame):
1423 if self.environment.sandboxed and \
1424 operator in self.environment.intercepted_binops:
1425 self.write('environment.call_binop(context, %r, ' % operator)
1426 self.visit(node.left, frame)
1427 self.write(', ')
1428 self.visit(node.right, frame)
1429 else:
1430 self.write('(')
1431 self.visit(node.left, frame)
1432 self.write(' %s ' % operator)
1433 self.visit(node.right, frame)
1434 self.write(')')
1435 return visitor
1436
1437 def uaop(operator, interceptable=True):
1438 def visitor(self, node, frame):
1439 if self.environment.sandboxed and \
1440 operator in self.environment.intercepted_unops:
1441 self.write('environment.call_unop(context, %r, ' % operator)
1442 self.visit(node.node, frame)
1443 else:
1444 self.write('(' + operator)
1445 self.visit(node.node, frame)
1446 self.write(')')
1447 return visitor
1448
1449 visit_Add = binop('+')
1450 visit_Sub = binop('-')
1451 visit_Mul = binop('*')
1452 visit_Div = binop('/')
1453 visit_FloorDiv = binop('//')
1454 visit_Pow = binop('**')
1455 visit_Mod = binop('%')
1456 visit_And = binop('and', interceptable=False)
1457 visit_Or = binop('or', interceptable=False)
1458 visit_Pos = uaop('+')
1459 visit_Neg = uaop('-')
1460 visit_Not = uaop('not ', interceptable=False)
1461 del binop, uaop
1462
1463 def visit_Concat(self, node, frame):
1464 if frame.eval_ctx.volatile:
1465 func_name = '(context.eval_ctx.volatile and' \
1466 ' markup_join or unicode_join)'
1467 elif frame.eval_ctx.autoescape:
1468 func_name = 'markup_join'
1469 else:
1470 func_name = 'unicode_join'
1471 self.write('%s((' % func_name)
1472 for arg in node.nodes:
1473 self.visit(arg, frame)
1474 self.write(', ')
1475 self.write('))')
1476
1477 def visit_Compare(self, node, frame):
1478 self.visit(node.expr, frame)
1479 for op in node.ops:
1480 self.visit(op, frame)
1481
1482 def visit_Operand(self, node, frame):
1483 self.write(' %s ' % operators[node.op])
1484 self.visit(node.expr, frame)
1485
1486 def visit_Getattr(self, node, frame):
1487 self.write('environment.getattr(')
1488 self.visit(node.node, frame)
1489 self.write(', %r)' % node.attr)
1490
1491 def visit_Getitem(self, node, frame):
1492 # slices bypass the environment getitem method.
1493 if isinstance(node.arg, nodes.Slice):
1494 self.visit(node.node, frame)
1495 self.write('[')
1496 self.visit(node.arg, frame)
1497 self.write(']')
1498 else:
1499 self.write('environment.getitem(')
1500 self.visit(node.node, frame)
1501 self.write(', ')
1502 self.visit(node.arg, frame)
1503 self.write(')')
1504
1505 def visit_Slice(self, node, frame):
1506 if node.start is not None:
1507 self.visit(node.start, frame)
1508 self.write(':')
1509 if node.stop is not None:
1510 self.visit(node.stop, frame)
1511 if node.step is not None:
1512 self.write(':')
1513 self.visit(node.step, frame)
1514
1515 def visit_Filter(self, node, frame):
1516 self.write(self.filters[node.name] + '(')
1517 func = self.environment.filters.get(node.name)
1518 if func is None:
1519 self.fail('no filter named %r' % node.name, node.lineno)
1520 if getattr(func, 'contextfilter', False):
1521 self.write('context, ')
1522 elif getattr(func, 'evalcontextfilter', False):
1523 self.write('context.eval_ctx, ')
1524 elif getattr(func, 'environmentfilter', False):
1525 self.write('environment, ')
1526
1527 # if the filter node is None we are inside a filter block
1528 # and want to write to the current buffer
1529 if node.node is not None:
1530 self.visit(node.node, frame)
1531 elif frame.eval_ctx.volatile:
1532 self.write('(context.eval_ctx.autoescape and'
1533 ' Markup(concat(%s)) or concat(%s))' %
1534 (frame.buffer, frame.buffer))
1535 elif frame.eval_ctx.autoescape:
1536 self.write('Markup(concat(%s))' % frame.buffer)
1537 else:
1538 self.write('concat(%s)' % frame.buffer)
1539 self.signature(node, frame)
1540 self.write(')')
1541
1542 def visit_Test(self, node, frame):
1543 self.write(self.tests[node.name] + '(')
1544 if node.name not in self.environment.tests:
1545 self.fail('no test named %r' % node.name, node.lineno)
1546 self.visit(node.node, frame)
1547 self.signature(node, frame)
1548 self.write(')')
1549
1550 def visit_CondExpr(self, node, frame):
1551 def write_expr2():
1552 if node.expr2 is not None:
1553 return self.visit(node.expr2, frame)
1554 self.write('environment.undefined(%r)' % ('the inline if-'
1555 'expression on %s evaluated to false and '
1556 'no else section was defined.' % self.position(node)))
1557
1558 self.write('(')
1559 self.visit(node.expr1, frame)
1560 self.write(' if ')
1561 self.visit(node.test, frame)
1562 self.write(' else ')
1563 write_expr2()
1564 self.write(')')
1565
1566 def visit_Call(self, node, frame, forward_caller=False):
1567 if self.environment.sandboxed:
1568 self.write('environment.call(context, ')
1569 else:
1570 self.write('context.call(')
1571 self.visit(node.node, frame)
1572 extra_kwargs = forward_caller and {'caller': 'caller'} or None
1573 self.signature(node, frame, extra_kwargs)
1574 self.write(')')
1575
1576 def visit_Keyword(self, node, frame):
1577 self.write(node.key + '=')
1578 self.visit(node.value, frame)
1579
1580 # -- Unused nodes for extensions
1581
1582 def visit_MarkSafe(self, node, frame):
1583 self.write('Markup(')
1584 self.visit(node.expr, frame)
1585 self.write(')')
1586
1587 def visit_MarkSafeIfAutoescape(self, node, frame):
1588 self.write('(context.eval_ctx.autoescape and Markup or identity)(')
1589 self.visit(node.expr, frame)
1590 self.write(')')
1591
1592 def visit_EnvironmentAttribute(self, node, frame):
1593 self.write('environment.' + node.name)
1594
1595 def visit_ExtensionAttribute(self, node, frame):
1596 self.write('environment.extensions[%r].%s' % (node.identifier, node.name ))
1597
1598 def visit_ImportedName(self, node, frame):
1599 self.write(self.import_aliases[node.importname])
1600
1601 def visit_InternalName(self, node, frame):
1602 self.write(node.name)
1603
1604 def visit_ContextReference(self, node, frame):
1605 self.write('context')
1606
1607 def visit_Continue(self, node, frame):
1608 self.writeline('continue', node)
1609
1610 def visit_Break(self, node, frame):
1611 self.writeline('break', node)
1612
1613 def visit_Scope(self, node, frame):
1614 scope_frame = frame.inner()
1615 scope_frame.inspect(node.iter_child_nodes())
1616 aliases = self.push_scope(scope_frame)
1617 self.pull_locals(scope_frame)
1618 self.blockvisit(node.body, scope_frame)
1619 self.pop_scope(aliases, scope_frame)
1620
1621 def visit_EvalContextModifier(self, node, frame):
1622 for keyword in node.options:
1623 self.writeline('context.eval_ctx.%s = ' % keyword.key)
1624 self.visit(keyword.value, frame)
1625 try:
1626 val = keyword.value.as_const(frame.eval_ctx)
1627 except nodes.Impossible:
1628 frame.eval_ctx.volatile = True
1629 else:
1630 setattr(frame.eval_ctx, keyword.key, val)
1631
1632 def visit_ScopedEvalContextModifier(self, node, frame):
1633 old_ctx_name = self.temporary_identifier()
1634 safed_ctx = frame.eval_ctx.save()
1635 self.writeline('%s = context.eval_ctx.save()' % old_ctx_name)
1636 self.visit_EvalContextModifier(node, frame)
1637 for child in node.body:
1638 self.visit(child, frame)
1639 frame.eval_ctx.revert(safed_ctx)
1640 self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name)
OLDNEW
« no previous file with comments | « mojo/public/third_party/jinja2/bccache.py ('k') | mojo/public/third_party/jinja2/constants.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698