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