| 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 |  | 
 | 8 |     :copyright: Copyright 2008 by Armin Ronacher. | 
 | 9 |     :license: GNU GPL. | 
 | 10 | """ | 
| Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 11 | from time import time | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 12 | from copy import copy | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 13 | from random import randrange | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 14 | from keyword import iskeyword | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 15 | from cStringIO import StringIO | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 16 | from itertools import chain | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 17 | from jinja2 import nodes | 
 | 18 | from jinja2.visitor import NodeVisitor, NodeTransformer | 
 | 19 | from jinja2.exceptions import TemplateAssertionError | 
| Armin Ronacher | 7ceced5 | 2008-05-03 10:15:31 +0200 | [diff] [blame] | 20 | from jinja2.utils import Markup, concat | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 21 |  | 
 | 22 |  | 
 | 23 | operators = { | 
 | 24 |     'eq':       '==', | 
 | 25 |     'ne':       '!=', | 
 | 26 |     'gt':       '>', | 
 | 27 |     'gteq':     '>=', | 
 | 28 |     'lt':       '<', | 
 | 29 |     'lteq':     '<=', | 
 | 30 |     'in':       'in', | 
 | 31 |     'notin':    'not in' | 
 | 32 | } | 
 | 33 |  | 
| Armin Ronacher | 3d8b784 | 2008-04-13 13:16:50 +0200 | [diff] [blame] | 34 | try: | 
 | 35 |     exec '(0 if 0 else 0)' | 
 | 36 | except SyntaxError: | 
 | 37 |     have_condexpr = False | 
 | 38 | else: | 
 | 39 |     have_condexpr = True | 
 | 40 |  | 
 | 41 |  | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 42 | def generate(node, environment, name, filename, stream=None): | 
| Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 43 |     """Generate the python source for a node tree.""" | 
| Armin Ronacher | 023b5e9 | 2008-05-08 11:03:10 +0200 | [diff] [blame] | 44 |     if not isinstance(node, nodes.Template): | 
 | 45 |         raise TypeError('Can\'t compile non template nodes') | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 46 |     generator = CodeGenerator(environment, name, filename, stream) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 47 |     generator.visit(node) | 
 | 48 |     if stream is None: | 
 | 49 |         return generator.stream.getvalue() | 
 | 50 |  | 
 | 51 |  | 
| Armin Ronacher | 4dfc975 | 2008-04-09 15:03:29 +0200 | [diff] [blame] | 52 | def has_safe_repr(value): | 
 | 53 |     """Does the node have a safe representation?""" | 
| Armin Ronacher | d55ab53 | 2008-04-09 16:13:39 +0200 | [diff] [blame] | 54 |     if value is None or value is NotImplemented or value is Ellipsis: | 
| Armin Ronacher | 4dfc975 | 2008-04-09 15:03:29 +0200 | [diff] [blame] | 55 |         return True | 
| Armin Ronacher | d55ab53 | 2008-04-09 16:13:39 +0200 | [diff] [blame] | 56 |     if isinstance(value, (bool, int, long, float, complex, basestring, | 
| Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 57 |                           xrange, Markup)): | 
| Armin Ronacher | 4dfc975 | 2008-04-09 15:03:29 +0200 | [diff] [blame] | 58 |         return True | 
| Armin Ronacher | d55ab53 | 2008-04-09 16:13:39 +0200 | [diff] [blame] | 59 |     if isinstance(value, (tuple, list, set, frozenset)): | 
| Armin Ronacher | 4dfc975 | 2008-04-09 15:03:29 +0200 | [diff] [blame] | 60 |         for item in value: | 
 | 61 |             if not has_safe_repr(item): | 
 | 62 |                 return False | 
 | 63 |         return True | 
 | 64 |     elif isinstance(value, dict): | 
 | 65 |         for key, value in value.iteritems(): | 
 | 66 |             if not has_safe_repr(key): | 
 | 67 |                 return False | 
 | 68 |             if not has_safe_repr(value): | 
 | 69 |                 return False | 
 | 70 |         return True | 
 | 71 |     return False | 
 | 72 |  | 
 | 73 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 74 | def find_undeclared(nodes, names): | 
 | 75 |     """Check if the names passed are accessed undeclared.  The return value | 
 | 76 |     is a set of all the undeclared names from the sequence of names found. | 
 | 77 |     """ | 
 | 78 |     visitor = UndeclaredNameVisitor(names) | 
 | 79 |     try: | 
 | 80 |         for node in nodes: | 
 | 81 |             visitor.visit(node) | 
 | 82 |     except VisitorExit: | 
 | 83 |         pass | 
 | 84 |     return visitor.undeclared | 
 | 85 |  | 
 | 86 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 87 | class Identifiers(object): | 
 | 88 |     """Tracks the status of identifiers in frames.""" | 
 | 89 |  | 
 | 90 |     def __init__(self): | 
 | 91 |         # variables that are known to be declared (probably from outer | 
 | 92 |         # frames or because they are special for the frame) | 
 | 93 |         self.declared = set() | 
 | 94 |  | 
| Armin Ronacher | 10f3ba2 | 2008-04-18 11:30:37 +0200 | [diff] [blame] | 95 |         # undeclared variables from outer scopes | 
 | 96 |         self.outer_undeclared = set() | 
 | 97 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 98 |         # names that are accessed without being explicitly declared by | 
 | 99 |         # this one or any of the outer scopes.  Names can appear both in | 
 | 100 |         # declared and undeclared. | 
 | 101 |         self.undeclared = set() | 
 | 102 |  | 
 | 103 |         # names that are declared locally | 
 | 104 |         self.declared_locally = set() | 
 | 105 |  | 
 | 106 |         # names that are declared by parameters | 
 | 107 |         self.declared_parameter = set() | 
 | 108 |  | 
 | 109 |     def add_special(self, name): | 
 | 110 |         """Register a special name like `loop`.""" | 
 | 111 |         self.undeclared.discard(name) | 
 | 112 |         self.declared.add(name) | 
 | 113 |  | 
| Armin Ronacher | 4f62a9f | 2008-04-08 18:09:13 +0200 | [diff] [blame] | 114 |     def is_declared(self, name, local_only=False): | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 115 |         """Check if a name is declared in this or an outer scope.""" | 
| Armin Ronacher | 4f62a9f | 2008-04-08 18:09:13 +0200 | [diff] [blame] | 116 |         if name in self.declared_locally or name in self.declared_parameter: | 
 | 117 |             return True | 
 | 118 |         if local_only: | 
 | 119 |             return False | 
 | 120 |         return name in self.declared | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 121 |  | 
 | 122 |     def find_shadowed(self): | 
 | 123 |         """Find all the shadowed names.""" | 
| Armin Ronacher | 10f3ba2 | 2008-04-18 11:30:37 +0200 | [diff] [blame] | 124 |         return (self.declared | self.outer_undeclared) & \ | 
 | 125 |                (self.declared_locally | self.declared_parameter) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 126 |  | 
 | 127 |  | 
 | 128 | class Frame(object): | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 129 |     """Holds compile time information for us.""" | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 130 |  | 
 | 131 |     def __init__(self, parent=None): | 
 | 132 |         self.identifiers = Identifiers() | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 133 |  | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 134 |         # a toplevel frame is the root + soft frames such as if conditions. | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 135 |         self.toplevel = False | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 136 |  | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 137 |         # the root frame is basically just the outermost frame, so no if | 
 | 138 |         # conditions.  This information is used to optimize inheritance | 
 | 139 |         # situations. | 
 | 140 |         self.rootlevel = False | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 141 |  | 
 | 142 |         # inside some tags we are using a buffer rather than yield statements. | 
 | 143 |         # this for example affects {% filter %} or {% macro %}.  If a frame | 
 | 144 |         # is buffered this variable points to the name of the list used as | 
 | 145 |         # buffer. | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 146 |         self.buffer = None | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 147 |  | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 148 |         # the name of the block we're in, otherwise None. | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 149 |         self.block = parent and parent.block or None | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 150 |  | 
 | 151 |         # the parent of this frame | 
 | 152 |         self.parent = parent | 
 | 153 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 154 |         if parent is not None: | 
 | 155 |             self.identifiers.declared.update( | 
 | 156 |                 parent.identifiers.declared | | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 157 |                 parent.identifiers.declared_locally | | 
 | 158 |                 parent.identifiers.declared_parameter | 
 | 159 |             ) | 
| Armin Ronacher | 10f3ba2 | 2008-04-18 11:30:37 +0200 | [diff] [blame] | 160 |             self.identifiers.outer_undeclared.update( | 
 | 161 |                 parent.identifiers.undeclared - | 
 | 162 |                 self.identifiers.declared | 
 | 163 |             ) | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 164 |             self.buffer = parent.buffer | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 165 |  | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 166 |     def copy(self): | 
 | 167 |         """Create a copy of the current one.""" | 
 | 168 |         rv = copy(self) | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 169 |         rv.identifiers = copy(self.identifiers) | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 170 |         return rv | 
 | 171 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 172 |     def inspect(self, nodes, hard_scope=False): | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 173 |         """Walk the node and check for identifiers.  If the scope is hard (eg: | 
 | 174 |         enforce on a python level) overrides from outer scopes are tracked | 
 | 175 |         differently. | 
| Armin Ronacher | 4f62a9f | 2008-04-08 18:09:13 +0200 | [diff] [blame] | 176 |         """ | 
 | 177 |         visitor = FrameIdentifierVisitor(self.identifiers, hard_scope) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 178 |         for node in nodes: | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 179 |             visitor.visit(node) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 180 |  | 
 | 181 |     def inner(self): | 
 | 182 |         """Return an inner frame.""" | 
 | 183 |         return Frame(self) | 
 | 184 |  | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 185 |     def soft(self): | 
 | 186 |         """Return a soft frame.  A soft frame may not be modified as | 
 | 187 |         standalone thing as it shares the resources with the frame it | 
 | 188 |         was created of, but it's not a rootlevel frame any longer. | 
 | 189 |         """ | 
 | 190 |         rv = copy(self) | 
 | 191 |         rv.rootlevel = False | 
 | 192 |         return rv | 
 | 193 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 194 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 195 | class VisitorExit(RuntimeError): | 
 | 196 |     """Exception used by the `UndeclaredNameVisitor` to signal a stop.""" | 
 | 197 |  | 
 | 198 |  | 
 | 199 | class DependencyFinderVisitor(NodeVisitor): | 
 | 200 |     """A visitor that collects filter and test calls.""" | 
 | 201 |  | 
 | 202 |     def __init__(self): | 
 | 203 |         self.filters = set() | 
 | 204 |         self.tests = set() | 
 | 205 |  | 
 | 206 |     def visit_Filter(self, node): | 
 | 207 |         self.generic_visit(node) | 
 | 208 |         self.filters.add(node.name) | 
 | 209 |  | 
 | 210 |     def visit_Test(self, node): | 
 | 211 |         self.generic_visit(node) | 
 | 212 |         self.tests.add(node.name) | 
 | 213 |  | 
 | 214 |     def visit_Block(self, node): | 
 | 215 |         """Stop visiting at blocks.""" | 
 | 216 |  | 
 | 217 |  | 
 | 218 | class UndeclaredNameVisitor(NodeVisitor): | 
 | 219 |     """A visitor that checks if a name is accessed without being | 
 | 220 |     declared.  This is different from the frame visitor as it will | 
 | 221 |     not stop at closure frames. | 
 | 222 |     """ | 
 | 223 |  | 
 | 224 |     def __init__(self, names): | 
 | 225 |         self.names = set(names) | 
 | 226 |         self.undeclared = set() | 
 | 227 |  | 
 | 228 |     def visit_Name(self, node): | 
 | 229 |         if node.ctx == 'load' and node.name in self.names: | 
 | 230 |             self.undeclared.add(node.name) | 
 | 231 |             if self.undeclared == self.names: | 
 | 232 |                 raise VisitorExit() | 
 | 233 |         else: | 
 | 234 |             self.names.discard(node.name) | 
 | 235 |  | 
 | 236 |     def visit_Block(self, node): | 
 | 237 |         """Stop visiting a blocks.""" | 
 | 238 |  | 
 | 239 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 240 | class FrameIdentifierVisitor(NodeVisitor): | 
 | 241 |     """A visitor for `Frame.inspect`.""" | 
 | 242 |  | 
| Armin Ronacher | 4f62a9f | 2008-04-08 18:09:13 +0200 | [diff] [blame] | 243 |     def __init__(self, identifiers, hard_scope): | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 244 |         self.identifiers = identifiers | 
| Armin Ronacher | 4f62a9f | 2008-04-08 18:09:13 +0200 | [diff] [blame] | 245 |         self.hard_scope = hard_scope | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 246 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 247 |     def visit_Name(self, node): | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 248 |         """All assignments to names go through this function.""" | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 249 |         if node.ctx in ('store', 'param'): | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 250 |             self.identifiers.declared_locally.add(node.name) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 251 |         elif node.ctx == 'load' and not \ | 
 | 252 |              self.identifiers.is_declared(node.name, self.hard_scope): | 
 | 253 |             self.identifiers.undeclared.add(node.name) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 254 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 255 |     def visit_Macro(self, node): | 
 | 256 |         self.generic_visit(node) | 
 | 257 |         self.identifiers.declared_locally.add(node.name) | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 258 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 259 |     def visit_Import(self, node): | 
 | 260 |         self.generic_visit(node) | 
 | 261 |         self.identifiers.declared_locally.add(node.target) | 
| Armin Ronacher | f059ec1 | 2008-04-11 22:21:00 +0200 | [diff] [blame] | 262 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 263 |     def visit_FromImport(self, node): | 
 | 264 |         self.generic_visit(node) | 
 | 265 |         for name in node.names: | 
 | 266 |             if isinstance(name, tuple): | 
 | 267 |                 self.identifiers.declared_locally.add(name[1]) | 
 | 268 |             else: | 
 | 269 |                 self.identifiers.declared_locally.add(name) | 
 | 270 |  | 
 | 271 |     def visit_Assign(self, node): | 
| Armin Ronacher | ebe55aa | 2008-04-10 20:51:23 +0200 | [diff] [blame] | 272 |         """Visit assignments in the correct order.""" | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 273 |         self.visit(node.node) | 
 | 274 |         self.visit(node.target) | 
| Armin Ronacher | ebe55aa | 2008-04-10 20:51:23 +0200 | [diff] [blame] | 275 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 276 |     def visit_For(self, node): | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 277 |         """Visiting stops at for blocks.  However the block sequence | 
 | 278 |         is visited as part of the outer scope. | 
 | 279 |         """ | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 280 |         self.visit(node.iter) | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 281 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 282 |     def visit_CallBlock(self, node): | 
 | 283 |         for child in node.iter_child_nodes(exclude=('body',)): | 
 | 284 |             self.visit(child) | 
 | 285 |  | 
 | 286 |     def visit_FilterBlock(self, node): | 
 | 287 |         self.visit(node.filter) | 
 | 288 |  | 
 | 289 |     def visit_Block(self, node): | 
 | 290 |         """Stop visiting at blocks.""" | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 291 |  | 
 | 292 |  | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 293 | class CompilerExit(Exception): | 
 | 294 |     """Raised if the compiler encountered a situation where it just | 
 | 295 |     doesn't make sense to further process the code.  Any block that | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 296 |     raises such an exception is not further processed. | 
 | 297 |     """ | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 298 |  | 
 | 299 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 300 | class CodeGenerator(NodeVisitor): | 
 | 301 |  | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 302 |     def __init__(self, environment, name, filename, stream=None): | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 303 |         if stream is None: | 
 | 304 |             stream = StringIO() | 
| Christoph Hack | 65642a5 | 2008-04-08 14:46:56 +0200 | [diff] [blame] | 305 |         self.environment = environment | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 306 |         self.name = name | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 307 |         self.filename = filename | 
 | 308 |         self.stream = stream | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 309 |  | 
| Armin Ronacher | 023b5e9 | 2008-05-08 11:03:10 +0200 | [diff] [blame] | 310 |         # aliases for imports | 
 | 311 |         self.import_aliases = {} | 
 | 312 |  | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 313 |         # a registry for all blocks.  Because blocks are moved out | 
 | 314 |         # into the global python scope they are registered here | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 315 |         self.blocks = {} | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 316 |  | 
 | 317 |         # the number of extends statements so far | 
| Armin Ronacher | 7fb3897 | 2008-04-11 13:54:28 +0200 | [diff] [blame] | 318 |         self.extends_so_far = 0 | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 319 |  | 
 | 320 |         # some templates have a rootlevel extends.  In this case we | 
 | 321 |         # can safely assume that we're a child template and do some | 
 | 322 |         # more optimizations. | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 323 |         self.has_known_extends = False | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 324 |  | 
| Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 325 |         # the current line number | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 326 |         self.code_lineno = 1 | 
| Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 327 |  | 
| Armin Ronacher | b9e7875 | 2008-05-10 23:36:28 +0200 | [diff] [blame] | 328 |         # registry of all filters and tests (global, not block local) | 
 | 329 |         self.tests = {} | 
 | 330 |         self.filters = {} | 
 | 331 |  | 
| Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 332 |         # the debug information | 
 | 333 |         self.debug_info = [] | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 334 |         self._write_debug_info = None | 
| Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 335 |  | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 336 |         # the number of new lines before the next write() | 
 | 337 |         self._new_lines = 0 | 
 | 338 |  | 
 | 339 |         # the line number of the last written statement | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 340 |         self._last_line = 0 | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 341 |  | 
 | 342 |         # true if nothing was written so far. | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 343 |         self._first_write = True | 
 | 344 |  | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 345 |         # used by the `temporary_identifier` method to get new | 
 | 346 |         # unique, temporary identifier | 
 | 347 |         self._last_identifier = 0 | 
 | 348 |  | 
 | 349 |         # the current indentation | 
 | 350 |         self._indentation = 0 | 
 | 351 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 352 |     def temporary_identifier(self): | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 353 |         """Get a new unique identifier.""" | 
 | 354 |         self._last_identifier += 1 | 
 | 355 |         return 't%d' % self._last_identifier | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 356 |  | 
 | 357 |     def indent(self): | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 358 |         """Indent by one.""" | 
 | 359 |         self._indentation += 1 | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 360 |  | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 361 |     def outdent(self, step=1): | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 362 |         """Outdent by step.""" | 
 | 363 |         self._indentation -= step | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 364 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 365 |     def blockvisit(self, nodes, frame, force_generator=True): | 
 | 366 |         """Visit a list of nodes as block in a frame.  If the current frame | 
 | 367 |         is no buffer a dummy ``if 0: yield None`` is written automatically | 
 | 368 |         unless the force_generator parameter is set to False. | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 369 |         """ | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 370 |         if frame.buffer is None and force_generator: | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 371 |             self.writeline('if 0: yield None') | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 372 |         try: | 
 | 373 |             for node in nodes: | 
 | 374 |                 self.visit(node, frame) | 
 | 375 |         except CompilerExit: | 
 | 376 |             pass | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 377 |  | 
 | 378 |     def write(self, x): | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 379 |         """Write a string into the output stream.""" | 
 | 380 |         if self._new_lines: | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 381 |             if not self._first_write: | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 382 |                 self.stream.write('\n' * self._new_lines) | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 383 |                 self.code_lineno += self._new_lines | 
 | 384 |                 if self._write_debug_info is not None: | 
 | 385 |                     self.debug_info.append((self._write_debug_info, | 
 | 386 |                                             self.code_lineno)) | 
 | 387 |                     self._write_debug_info = None | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 388 |             self._first_write = False | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 389 |             self.stream.write('    ' * self._indentation) | 
 | 390 |             self._new_lines = 0 | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 391 |         self.stream.write(x) | 
 | 392 |  | 
 | 393 |     def writeline(self, x, node=None, extra=0): | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 394 |         """Combination of newline and write.""" | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 395 |         self.newline(node, extra) | 
 | 396 |         self.write(x) | 
 | 397 |  | 
 | 398 |     def newline(self, node=None, extra=0): | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 399 |         """Add one or more newlines before the next write.""" | 
 | 400 |         self._new_lines = max(self._new_lines, 1 + extra) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 401 |         if node is not None and node.lineno != self._last_line: | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 402 |             self._write_debug_info = node.lineno | 
 | 403 |             self._last_line = node.lineno | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 404 |  | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 405 |     def signature(self, node, frame, have_comma=True, extra_kwargs=None): | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 406 |         """Writes a function call to the stream for the current node. | 
 | 407 |         Per default it will write a leading comma but this can be | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 408 |         disabled by setting have_comma to False.  The extra keyword | 
 | 409 |         arguments may not include python keywords otherwise a syntax | 
 | 410 |         error could occour.  The extra keyword arguments should be given | 
 | 411 |         as python dict. | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 412 |         """ | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 413 |         have_comma = have_comma and [True] or [] | 
 | 414 |         def touch_comma(): | 
 | 415 |             if have_comma: | 
 | 416 |                 self.write(', ') | 
 | 417 |             else: | 
 | 418 |                 have_comma.append(True) | 
 | 419 |  | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 420 |         # if any of the given keyword arguments is a python keyword | 
 | 421 |         # we have to make sure that no invalid call is created. | 
 | 422 |         kwarg_workaround = False | 
 | 423 |         for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()): | 
 | 424 |             if iskeyword(kwarg): | 
 | 425 |                 kwarg_workaround = True | 
 | 426 |                 break | 
 | 427 |  | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 428 |         for arg in node.args: | 
 | 429 |             touch_comma() | 
 | 430 |             self.visit(arg, frame) | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 431 |  | 
 | 432 |         if not kwarg_workaround: | 
 | 433 |             for kwarg in node.kwargs: | 
 | 434 |                 touch_comma() | 
 | 435 |                 self.visit(kwarg, frame) | 
 | 436 |             if extra_kwargs is not None: | 
 | 437 |                 for key, value in extra_kwargs.iteritems(): | 
 | 438 |                     touch_comma() | 
 | 439 |                     self.write('%s=%s' % (key, value)) | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 440 |         if node.dyn_args: | 
 | 441 |             touch_comma() | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 442 |             self.write('*') | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 443 |             self.visit(node.dyn_args, frame) | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 444 |  | 
 | 445 |         if kwarg_workaround: | 
 | 446 |             touch_comma() | 
 | 447 |             if node.dyn_kwargs is not None: | 
 | 448 |                 self.write('**dict({') | 
 | 449 |             else: | 
 | 450 |                 self.write('**{') | 
 | 451 |             for kwarg in node.kwargs: | 
 | 452 |                 self.write('%r: ' % kwarg.key) | 
 | 453 |                 self.visit(kwarg.value, frame) | 
 | 454 |                 self.write(', ') | 
 | 455 |             if extra_kwargs is not None: | 
 | 456 |                 for key, value in extra_kwargs.iteritems(): | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 457 |                     self.write('%r: %s, ' % (key, value)) | 
 | 458 |             if node.dyn_kwargs is not None: | 
 | 459 |                 self.write('}, **') | 
 | 460 |                 self.visit(node.dyn_kwargs, frame) | 
 | 461 |                 self.write(')') | 
 | 462 |             else: | 
 | 463 |                 self.write('}') | 
 | 464 |  | 
 | 465 |         elif node.dyn_kwargs is not None: | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 466 |             touch_comma() | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 467 |             self.write('**') | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 468 |             self.visit(node.dyn_kwargs, frame) | 
 | 469 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 470 |     def pull_locals(self, frame): | 
 | 471 |         """Pull all the references identifiers into the local scope.""" | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 472 |         for name in frame.identifiers.undeclared: | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 473 |             self.writeline('l_%s = context.resolve(%r)' % (name, name)) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 474 |  | 
 | 475 |     def pull_dependencies(self, nodes): | 
 | 476 |         """Pull all the dependencies.""" | 
 | 477 |         visitor = DependencyFinderVisitor() | 
 | 478 |         for node in nodes: | 
 | 479 |             visitor.visit(node) | 
| Armin Ronacher | b9e7875 | 2008-05-10 23:36:28 +0200 | [diff] [blame] | 480 |         for dependency in 'filters', 'tests': | 
 | 481 |             mapping = getattr(self, dependency) | 
 | 482 |             for name in getattr(visitor, dependency): | 
 | 483 |                 if name not in mapping: | 
 | 484 |                     mapping[name] = self.temporary_identifier() | 
 | 485 |                 self.writeline('%s = environment.%s[%r]' % | 
 | 486 |                                (mapping[name], dependency, name)) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 487 |  | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 488 |     def collect_shadowed(self, frame): | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 489 |         """This function returns all the shadowed variables in a dict | 
 | 490 |         in the form name: alias and will write the required assignments | 
 | 491 |         into the current scope.  No indentation takes place. | 
 | 492 |         """ | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 493 |         # make sure we "backup" overridden, local identifiers | 
 | 494 |         # TODO: we should probably optimize this and check if the | 
 | 495 |         # identifier is in use afterwards. | 
 | 496 |         aliases = {} | 
 | 497 |         for name in frame.identifiers.find_shadowed(): | 
 | 498 |             aliases[name] = ident = self.temporary_identifier() | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 499 |             self.writeline('%s = l_%s' % (ident, name)) | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 500 |         return aliases | 
 | 501 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 502 |     def function_scoping(self, node, frame, children=None): | 
| Armin Ronacher | fed44b5 | 2008-04-13 19:42:53 +0200 | [diff] [blame] | 503 |         """In Jinja a few statements require the help of anonymous | 
 | 504 |         functions.  Those are currently macros and call blocks and in | 
 | 505 |         the future also recursive loops.  As there is currently | 
 | 506 |         technical limitation that doesn't allow reading and writing a | 
 | 507 |         variable in a scope where the initial value is coming from an | 
 | 508 |         outer scope, this function tries to fall back with a common | 
 | 509 |         error message.  Additionally the frame passed is modified so | 
 | 510 |         that the argumetns are collected and callers are looked up. | 
 | 511 |  | 
 | 512 |         This will return the modified frame. | 
 | 513 |         """ | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 514 |         # we have to iterate twice over it, make sure that works | 
 | 515 |         if children is None: | 
 | 516 |             children = node.iter_child_nodes() | 
 | 517 |         children = list(children) | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 518 |         func_frame = frame.inner() | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 519 |         func_frame.inspect(children, hard_scope=True) | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 520 |  | 
 | 521 |         # variables that are undeclared (accessed before declaration) and | 
 | 522 |         # declared locally *and* part of an outside scope raise a template | 
 | 523 |         # assertion error. Reason: we can't generate reasonable code from | 
 | 524 |         # it without aliasing all the variables.  XXX: alias them ^^ | 
 | 525 |         overriden_closure_vars = ( | 
 | 526 |             func_frame.identifiers.undeclared & | 
 | 527 |             func_frame.identifiers.declared & | 
 | 528 |             (func_frame.identifiers.declared_locally | | 
 | 529 |              func_frame.identifiers.declared_parameter) | 
 | 530 |         ) | 
 | 531 |         if overriden_closure_vars: | 
 | 532 |             vars = ', '.join(sorted(overriden_closure_vars)) | 
 | 533 |             raise TemplateAssertionError('It\'s not possible to set and ' | 
 | 534 |                                          'access variables derived from ' | 
 | 535 |                                          'an outer scope! (affects: %s' % | 
| Armin Ronacher | 68f7767 | 2008-04-17 11:50:39 +0200 | [diff] [blame] | 536 |                                          vars, node.lineno, self.name) | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 537 |  | 
 | 538 |         # remove variables from a closure from the frame's undeclared | 
 | 539 |         # identifiers. | 
 | 540 |         func_frame.identifiers.undeclared -= ( | 
 | 541 |             func_frame.identifiers.undeclared & | 
 | 542 |             func_frame.identifiers.declared | 
 | 543 |         ) | 
 | 544 |  | 
| Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 545 |         func_frame.accesses_kwargs = False | 
 | 546 |         func_frame.accesses_varargs = False | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 547 |         func_frame.accesses_caller = False | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 548 |         func_frame.arguments = args = ['l_' + x.name for x in node.args] | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 549 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 550 |         undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs')) | 
 | 551 |  | 
 | 552 |         if 'caller' in undeclared: | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 553 |             func_frame.accesses_caller = True | 
 | 554 |             func_frame.identifiers.add_special('caller') | 
 | 555 |             args.append('l_caller') | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 556 |         if 'kwargs' in undeclared: | 
| Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 557 |             func_frame.accesses_kwargs = True | 
 | 558 |             func_frame.identifiers.add_special('kwargs') | 
 | 559 |             args.append('l_kwargs') | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 560 |         if 'varargs' in undeclared: | 
| Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 561 |             func_frame.accesses_varargs = True | 
 | 562 |             func_frame.identifiers.add_special('varargs') | 
 | 563 |             args.append('l_varargs') | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 564 |         return func_frame | 
 | 565 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 566 |     # -- Visitors | 
 | 567 |  | 
 | 568 |     def visit_Template(self, node, frame=None): | 
 | 569 |         assert frame is None, 'no root frame allowed' | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 570 |         from jinja2.runtime import __all__ as exported | 
| Armin Ronacher | 709f6e5 | 2008-04-28 18:18:16 +0200 | [diff] [blame] | 571 |         self.writeline('from __future__ import division') | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 572 |         self.writeline('from jinja2.runtime import ' + ', '.join(exported)) | 
| Armin Ronacher | 8edbe49 | 2008-04-10 20:43:43 +0200 | [diff] [blame] | 573 |  | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 574 |         # do we have an extends tag at all?  If not, we can save some | 
 | 575 |         # overhead by just not processing any inheritance code. | 
 | 576 |         have_extends = node.find(nodes.Extends) is not None | 
 | 577 |  | 
| Armin Ronacher | 8edbe49 | 2008-04-10 20:43:43 +0200 | [diff] [blame] | 578 |         # find all blocks | 
 | 579 |         for block in node.find_all(nodes.Block): | 
 | 580 |             if block.name in self.blocks: | 
 | 581 |                 raise TemplateAssertionError('block %r defined twice' % | 
 | 582 |                                              block.name, block.lineno, | 
| Armin Ronacher | 68f7767 | 2008-04-17 11:50:39 +0200 | [diff] [blame] | 583 |                                              self.name) | 
| Armin Ronacher | 8edbe49 | 2008-04-10 20:43:43 +0200 | [diff] [blame] | 584 |             self.blocks[block.name] = block | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 585 |  | 
| Armin Ronacher | 023b5e9 | 2008-05-08 11:03:10 +0200 | [diff] [blame] | 586 |         # find all imports and import them | 
 | 587 |         for import_ in node.find_all(nodes.ImportedName): | 
 | 588 |             if import_.importname not in self.import_aliases: | 
 | 589 |                 imp = import_.importname | 
 | 590 |                 self.import_aliases[imp] = alias = self.temporary_identifier() | 
 | 591 |                 if '.' in imp: | 
 | 592 |                     module, obj = imp.rsplit('.', 1) | 
 | 593 |                     self.writeline('from %s import %s as %s' % | 
 | 594 |                                    (module, obj, alias)) | 
 | 595 |                 else: | 
 | 596 |                     self.writeline('import %s as %s' % (imp, alias)) | 
 | 597 |  | 
 | 598 |         # add the load name | 
 | 599 |         self.writeline('name = %r' % self.name) | 
 | 600 |  | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 601 |         # generate the root render function. | 
| Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 602 |         self.writeline('def root(context, environment=environment):', extra=1) | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 603 |  | 
 | 604 |         # process the root | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 605 |         frame = Frame() | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 606 |         frame.inspect(node.body) | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 607 |         frame.toplevel = frame.rootlevel = True | 
| Armin Ronacher | f059ec1 | 2008-04-11 22:21:00 +0200 | [diff] [blame] | 608 |         self.indent() | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 609 |         if have_extends: | 
 | 610 |             self.writeline('parent_template = None') | 
 | 611 |         self.pull_locals(frame) | 
 | 612 |         self.pull_dependencies(node.body) | 
 | 613 |         if 'self' in find_undeclared(node.body, ('self',)): | 
 | 614 |             frame.identifiers.add_special('self') | 
 | 615 |             self.writeline('l_self = TemplateReference(context)') | 
 | 616 |         self.blockvisit(node.body, frame) | 
| Armin Ronacher | f059ec1 | 2008-04-11 22:21:00 +0200 | [diff] [blame] | 617 |         self.outdent() | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 618 |  | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 619 |         # make sure that the parent root is called. | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 620 |         if have_extends: | 
 | 621 |             if not self.has_known_extends: | 
 | 622 |                 self.indent() | 
| Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 623 |                 self.writeline('if parent_template is not None:') | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 624 |             self.indent() | 
| Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 625 |             self.writeline('for event in parent_template.' | 
 | 626 |                            'root_render_func(context):') | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 627 |             self.indent() | 
 | 628 |             self.writeline('yield event') | 
| Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 629 |             self.outdent(2 + (not self.has_known_extends)) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 630 |  | 
 | 631 |         # at this point we now have the blocks collected and can visit them too. | 
 | 632 |         for name, block in self.blocks.iteritems(): | 
 | 633 |             block_frame = Frame() | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 634 |             block_frame.inspect(block.body) | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 635 |             block_frame.block = name | 
| Armin Ronacher | d4c64f7 | 2008-04-11 17:15:29 +0200 | [diff] [blame] | 636 |             self.writeline('def block_%s(context, environment=environment):' | 
 | 637 |                            % name, block, 1) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 638 |             self.indent() | 
 | 639 |             undeclared = find_undeclared(block.body, ('self', 'super')) | 
 | 640 |             if 'self' in undeclared: | 
 | 641 |                 block_frame.identifiers.add_special('self') | 
 | 642 |                 self.writeline('l_self = TemplateReference(context)') | 
 | 643 |             if 'super' in undeclared: | 
 | 644 |                 block_frame.identifiers.add_special('super') | 
 | 645 |                 self.writeline('l_super = context.super(%r, ' | 
 | 646 |                                'block_%s)' % (name, name)) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 647 |             self.pull_locals(block_frame) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 648 |             self.pull_dependencies(block.body) | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 649 |             self.blockvisit(block.body, block_frame) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 650 |             self.outdent() | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 651 |  | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 652 |         self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x) | 
| Armin Ronacher | ba3757b | 2008-04-16 19:43:16 +0200 | [diff] [blame] | 653 |                                                    for x in self.blocks), | 
 | 654 |                        extra=1) | 
 | 655 |  | 
 | 656 |         # add a function that returns the debug info | 
| Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 657 |         self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x | 
 | 658 |                                                     in self.debug_info)) | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 659 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 660 |     def visit_Block(self, node, frame): | 
 | 661 |         """Call a block and register it for the template.""" | 
| Armin Ronacher | 41ef36f | 2008-04-11 19:55:08 +0200 | [diff] [blame] | 662 |         level = 1 | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 663 |         if frame.toplevel: | 
| Armin Ronacher | bcb7c53 | 2008-04-11 16:30:34 +0200 | [diff] [blame] | 664 |             # if we know that we are a child template, there is no need to | 
 | 665 |             # check if we are one | 
 | 666 |             if self.has_known_extends: | 
 | 667 |                 return | 
| Armin Ronacher | 41ef36f | 2008-04-11 19:55:08 +0200 | [diff] [blame] | 668 |             if self.extends_so_far > 0: | 
| Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 669 |                 self.writeline('if parent_template is None:') | 
| Armin Ronacher | 41ef36f | 2008-04-11 19:55:08 +0200 | [diff] [blame] | 670 |                 self.indent() | 
 | 671 |                 level += 1 | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 672 |         self.writeline('for event in context.blocks[%r][-1](context):' % | 
 | 673 |                        node.name, node) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 674 |         self.indent() | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 675 |         if frame.buffer is None: | 
 | 676 |             self.writeline('yield event') | 
 | 677 |         else: | 
 | 678 |             self.writeline('%s.append(event)' % frame.buffer) | 
| Armin Ronacher | 41ef36f | 2008-04-11 19:55:08 +0200 | [diff] [blame] | 679 |         self.outdent(level) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 680 |  | 
 | 681 |     def visit_Extends(self, node, frame): | 
 | 682 |         """Calls the extender.""" | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 683 |         if not frame.toplevel: | 
 | 684 |             raise TemplateAssertionError('cannot use extend from a non ' | 
 | 685 |                                          'top-level scope', node.lineno, | 
| Armin Ronacher | 68f7767 | 2008-04-17 11:50:39 +0200 | [diff] [blame] | 686 |                                          self.name) | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 687 |  | 
| Armin Ronacher | 7fb3897 | 2008-04-11 13:54:28 +0200 | [diff] [blame] | 688 |         # if the number of extends statements in general is zero so | 
 | 689 |         # far, we don't have to add a check if something extended | 
 | 690 |         # the template before this one. | 
 | 691 |         if self.extends_so_far > 0: | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 692 |  | 
| Armin Ronacher | 7fb3897 | 2008-04-11 13:54:28 +0200 | [diff] [blame] | 693 |             # if we have a known extends we just add a template runtime | 
 | 694 |             # error into the generated code.  We could catch that at compile | 
 | 695 |             # time too, but i welcome it not to confuse users by throwing the | 
 | 696 |             # same error at different times just "because we can". | 
 | 697 |             if not self.has_known_extends: | 
| Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 698 |                 self.writeline('if parent_template is not None:') | 
| Armin Ronacher | 7fb3897 | 2008-04-11 13:54:28 +0200 | [diff] [blame] | 699 |                 self.indent() | 
 | 700 |             self.writeline('raise TemplateRuntimeError(%r)' % | 
 | 701 |                            'extended multiple times') | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 702 |  | 
| Armin Ronacher | 7fb3897 | 2008-04-11 13:54:28 +0200 | [diff] [blame] | 703 |             # if we have a known extends already we don't need that code here | 
 | 704 |             # as we know that the template execution will end here. | 
 | 705 |             if self.has_known_extends: | 
 | 706 |                 raise CompilerExit() | 
 | 707 |             self.outdent() | 
 | 708 |  | 
| Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 709 |         self.writeline('parent_template = environment.get_template(', node, 1) | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 710 |         self.visit(node.template, frame) | 
| Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 711 |         self.write(', %r)' % self.name) | 
 | 712 |         self.writeline('for name, parent_block in parent_template.' | 
 | 713 |                        'blocks.iteritems():') | 
 | 714 |         self.indent() | 
 | 715 |         self.writeline('context.blocks.setdefault(name, []).' | 
 | 716 |                        'insert(0, parent_block)') | 
 | 717 |         self.outdent() | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 718 |  | 
 | 719 |         # if this extends statement was in the root level we can take | 
 | 720 |         # advantage of that information and simplify the generated code | 
 | 721 |         # in the top level from this point onwards | 
| Armin Ronacher | 27069d7 | 2008-05-11 19:48:12 +0200 | [diff] [blame^] | 722 |         if frame.rootlevel: | 
 | 723 |             self.has_known_extends = True | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 724 |  | 
| Armin Ronacher | 7fb3897 | 2008-04-11 13:54:28 +0200 | [diff] [blame] | 725 |         # and now we have one more | 
 | 726 |         self.extends_so_far += 1 | 
 | 727 |  | 
| Armin Ronacher | f059ec1 | 2008-04-11 22:21:00 +0200 | [diff] [blame] | 728 |     def visit_Include(self, node, frame): | 
 | 729 |         """Handles includes.""" | 
| Armin Ronacher | ea847c5 | 2008-05-02 20:04:32 +0200 | [diff] [blame] | 730 |         if node.with_context: | 
 | 731 |             self.writeline('template = environment.get_template(', node) | 
 | 732 |             self.visit(node.template, frame) | 
 | 733 |             self.write(', %r)' % self.name) | 
 | 734 |             self.writeline('for event in template.root_render_func(' | 
 | 735 |                            'template.new_context(context.parent, True)):') | 
 | 736 |         else: | 
 | 737 |             self.writeline('for event in environment.get_template(', node) | 
 | 738 |             self.visit(node.template, frame) | 
 | 739 |             self.write(', %r).module._TemplateModule__body_stream:' % | 
 | 740 |                        self.name) | 
| Armin Ronacher | f059ec1 | 2008-04-11 22:21:00 +0200 | [diff] [blame] | 741 |         self.indent() | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 742 |         if frame.buffer is None: | 
 | 743 |             self.writeline('yield event') | 
 | 744 |         else: | 
 | 745 |             self.writeline('%s.append(event)' % frame.buffer) | 
| Armin Ronacher | f059ec1 | 2008-04-11 22:21:00 +0200 | [diff] [blame] | 746 |         self.outdent() | 
 | 747 |  | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 748 |     def visit_Import(self, node, frame): | 
 | 749 |         """Visit regular imports.""" | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 750 |         self.writeline('l_%s = ' % node.target, node) | 
| Armin Ronacher | f059ec1 | 2008-04-11 22:21:00 +0200 | [diff] [blame] | 751 |         if frame.toplevel: | 
| Armin Ronacher | 5304229 | 2008-04-26 18:30:19 +0200 | [diff] [blame] | 752 |             self.write('context.vars[%r] = ' % node.target) | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 753 |         self.write('environment.get_template(') | 
 | 754 |         self.visit(node.template, frame) | 
| Armin Ronacher | ea847c5 | 2008-05-02 20:04:32 +0200 | [diff] [blame] | 755 |         self.write(', %r).' % self.name) | 
 | 756 |         if node.with_context: | 
 | 757 |             self.write('make_module(context.parent, True)') | 
 | 758 |         else: | 
 | 759 |             self.write('module') | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 760 |         if frame.toplevel and not node.target.startswith('__'): | 
| Armin Ronacher | 5304229 | 2008-04-26 18:30:19 +0200 | [diff] [blame] | 761 |             self.writeline('context.exported_vars.discard(%r)' % node.target) | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 762 |  | 
 | 763 |     def visit_FromImport(self, node, frame): | 
 | 764 |         """Visit named imports.""" | 
 | 765 |         self.newline(node) | 
 | 766 |         self.write('included_template = environment.get_template(') | 
 | 767 |         self.visit(node.template, frame) | 
| Armin Ronacher | ea847c5 | 2008-05-02 20:04:32 +0200 | [diff] [blame] | 768 |         self.write(', %r).' % self.name) | 
 | 769 |         if node.with_context: | 
 | 770 |             self.write('make_module(context.parent, True)') | 
 | 771 |         else: | 
 | 772 |             self.write('module') | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 773 |         for name in node.names: | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 774 |             if isinstance(name, tuple): | 
 | 775 |                 name, alias = name | 
 | 776 |             else: | 
 | 777 |                 alias = name | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 778 |             self.writeline('l_%s = getattr(included_template, ' | 
 | 779 |                            '%r, missing)' % (alias, name)) | 
 | 780 |             self.writeline('if l_%s is missing:' % alias) | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 781 |             self.indent() | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 782 |             self.writeline('l_%s = environment.undefined(%r %% ' | 
| Armin Ronacher | 19cf9c2 | 2008-05-01 12:49:53 +0200 | [diff] [blame] | 783 |                            'included_template.name, ' | 
 | 784 |                            'name=included_template.name)' % | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 785 |                            (alias, 'the template %r does not export ' | 
 | 786 |                             'the requested name ' + repr(name))) | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 787 |             self.outdent() | 
 | 788 |             if frame.toplevel: | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 789 |                 self.writeline('context.vars[%r] = l_%s' % (alias, alias)) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 790 |                 if not alias.startswith('__'): | 
 | 791 |                     self.writeline('context.exported_vars.discard(%r)' % alias) | 
| Armin Ronacher | f059ec1 | 2008-04-11 22:21:00 +0200 | [diff] [blame] | 792 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 793 |     def visit_For(self, node, frame): | 
 | 794 |         loop_frame = frame.inner() | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 795 |         loop_frame.inspect(node.iter_child_nodes(exclude=('iter',))) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 796 |         extended_loop = bool(node.else_) or \ | 
 | 797 |                         'loop' in loop_frame.identifiers.undeclared | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 798 |         if extended_loop: | 
 | 799 |             loop_frame.identifiers.add_special('loop') | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 800 |  | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 801 |         aliases = self.collect_shadowed(loop_frame) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 802 |         self.pull_locals(loop_frame) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 803 |         if node.else_: | 
 | 804 |             self.writeline('l_loop = None') | 
| Armin Ronacher | 3d8b784 | 2008-04-13 13:16:50 +0200 | [diff] [blame] | 805 |  | 
 | 806 |         self.newline(node) | 
 | 807 |         self.writeline('for ') | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 808 |         self.visit(node.target, loop_frame) | 
| Armin Ronacher | 180a1bd | 2008-04-09 12:14:24 +0200 | [diff] [blame] | 809 |         self.write(extended_loop and ', l_loop in LoopContext(' or ' in ') | 
| Armin Ronacher | 3d8b784 | 2008-04-13 13:16:50 +0200 | [diff] [blame] | 810 |  | 
 | 811 |         # the expression pointing to the parent loop.  We make the | 
 | 812 |         # undefined a bit more debug friendly at the same time. | 
 | 813 |         parent_loop = 'loop' in aliases and aliases['loop'] \ | 
| Armin Ronacher | 19cf9c2 | 2008-05-01 12:49:53 +0200 | [diff] [blame] | 814 |                       or "environment.undefined(%r, name='loop')" % "'loop' " \ | 
 | 815 |                          'is undefined. "the filter section of a loop as well ' \ | 
 | 816 |                          'as the else block doesn\'t have access to the ' \ | 
 | 817 |                          "special 'loop' variable of the current loop.  " \ | 
 | 818 |                          "Because there is no parent loop it's undefined." | 
| Armin Ronacher | 3d8b784 | 2008-04-13 13:16:50 +0200 | [diff] [blame] | 819 |  | 
 | 820 |         # if we have an extened loop and a node test, we filter in the | 
 | 821 |         # "outer frame". | 
 | 822 |         if extended_loop and node.test is not None: | 
 | 823 |             self.write('(') | 
 | 824 |             self.visit(node.target, loop_frame) | 
 | 825 |             self.write(' for ') | 
 | 826 |             self.visit(node.target, loop_frame) | 
 | 827 |             self.write(' in ') | 
 | 828 |             self.visit(node.iter, loop_frame) | 
 | 829 |             self.write(' if (') | 
 | 830 |             test_frame = loop_frame.copy() | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 831 |             self.writeline('l_loop = ' + parent_loop) | 
| Armin Ronacher | 3d8b784 | 2008-04-13 13:16:50 +0200 | [diff] [blame] | 832 |             self.visit(node.test, test_frame) | 
 | 833 |             self.write('))') | 
 | 834 |  | 
 | 835 |         else: | 
 | 836 |             self.visit(node.iter, loop_frame) | 
 | 837 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 838 |         self.write(extended_loop and '):' or ':') | 
| Armin Ronacher | 3d8b784 | 2008-04-13 13:16:50 +0200 | [diff] [blame] | 839 |  | 
 | 840 |         # tests in not extended loops become a continue | 
 | 841 |         if not extended_loop and node.test is not None: | 
 | 842 |             self.indent() | 
| Armin Ronacher | 47a506f | 2008-05-06 12:17:23 +0200 | [diff] [blame] | 843 |             self.writeline('if not ') | 
| Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 844 |             self.visit(node.test, loop_frame) | 
| Armin Ronacher | 3d8b784 | 2008-04-13 13:16:50 +0200 | [diff] [blame] | 845 |             self.write(':') | 
 | 846 |             self.indent() | 
 | 847 |             self.writeline('continue') | 
 | 848 |             self.outdent(2) | 
 | 849 |  | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 850 |         self.indent() | 
| Armin Ronacher | be4ae24 | 2008-04-18 09:49:08 +0200 | [diff] [blame] | 851 |         self.blockvisit(node.body, loop_frame, force_generator=True) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 852 |         self.outdent() | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 853 |  | 
 | 854 |         if node.else_: | 
 | 855 |             self.writeline('if l_loop is None:') | 
| Armin Ronacher | 3d8b784 | 2008-04-13 13:16:50 +0200 | [diff] [blame] | 856 |             self.indent() | 
 | 857 |             self.writeline('l_loop = ' + parent_loop) | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 858 |             self.blockvisit(node.else_, loop_frame, force_generator=False) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 859 |             self.outdent() | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 860 |  | 
| Armin Ronacher | d4c64f7 | 2008-04-11 17:15:29 +0200 | [diff] [blame] | 861 |         # reset the aliases if there are any. | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 862 |         for name, alias in aliases.iteritems(): | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 863 |             self.writeline('l_%s = %s' % (name, alias)) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 864 |  | 
 | 865 |     def visit_If(self, node, frame): | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 866 |         if_frame = frame.soft() | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 867 |         self.writeline('if ', node) | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 868 |         self.visit(node.test, if_frame) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 869 |         self.write(':') | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 870 |         self.indent() | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 871 |         self.blockvisit(node.body, if_frame) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 872 |         self.outdent() | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 873 |         if node.else_: | 
 | 874 |             self.writeline('else:') | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 875 |             self.indent() | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 876 |             self.blockvisit(node.else_, if_frame) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 877 |             self.outdent() | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 878 |  | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 879 |     def visit_Macro(self, node, frame): | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 880 |         macro_frame = self.function_scoping(node, frame) | 
 | 881 |         args = macro_frame.arguments | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 882 |         self.writeline('def macro(%s):' % ', '.join(args), node) | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 883 |         macro_frame.buffer = buf = self.temporary_identifier() | 
 | 884 |         self.indent() | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 885 |         self.pull_locals(macro_frame) | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 886 |         self.writeline('%s = []' % buf) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 887 |         self.blockvisit(node.body, macro_frame) | 
| Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame] | 888 |         if self.environment.autoescape: | 
 | 889 |             self.writeline('return Markup(concat(%s))' % buf) | 
 | 890 |         else: | 
 | 891 |             self.writeline("return concat(%s)" % buf) | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 892 |         self.outdent() | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 893 |         self.newline() | 
 | 894 |         if frame.toplevel: | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 895 |             if not node.name.startswith('__'): | 
 | 896 |                 self.write('context.exported_vars.add(%r)' % node.name) | 
| Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 897 |             self.writeline('context.vars[%r] = ' % node.name) | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 898 |         arg_tuple = ', '.join(repr(x.name) for x in node.args) | 
 | 899 |         if len(node.args) == 1: | 
 | 900 |             arg_tuple += ',' | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 901 |         self.write('l_%s = Macro(environment, macro, %r, (%s), (' % | 
 | 902 |                    (node.name, node.name, arg_tuple)) | 
| Armin Ronacher | 4f62a9f | 2008-04-08 18:09:13 +0200 | [diff] [blame] | 903 |         for arg in node.defaults: | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 904 |             self.visit(arg, macro_frame) | 
| Armin Ronacher | 4f62a9f | 2008-04-08 18:09:13 +0200 | [diff] [blame] | 905 |             self.write(', ') | 
| Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 906 |         self.write('), %s, %s, %s)' % ( | 
 | 907 |             macro_frame.accesses_kwargs and '1' or '0', | 
 | 908 |             macro_frame.accesses_varargs and '1' or '0', | 
| Armin Ronacher | 00d5d21 | 2008-04-13 01:10:18 +0200 | [diff] [blame] | 909 |             macro_frame.accesses_caller and '1' or '0' | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 910 |         )) | 
 | 911 |  | 
 | 912 |     def visit_CallBlock(self, node, frame): | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 913 |         call_frame = self.function_scoping(node, frame, node.iter_child_nodes | 
 | 914 |                                            (exclude=('call',))) | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 915 |         args = call_frame.arguments | 
 | 916 |         self.writeline('def call(%s):' % ', '.join(args), node) | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 917 |         call_frame.buffer = buf = self.temporary_identifier() | 
 | 918 |         self.indent() | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 919 |         self.pull_locals(call_frame) | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 920 |         self.writeline('%s = []' % buf) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 921 |         self.blockvisit(node.body, call_frame) | 
| Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame] | 922 |         if self.environment.autoescape: | 
 | 923 |             self.writeline("return Markup(concat(%s))" % buf) | 
 | 924 |         else: | 
 | 925 |             self.writeline('return concat(%s)' % buf) | 
| Armin Ronacher | 625215e | 2008-04-13 16:31:08 +0200 | [diff] [blame] | 926 |         self.outdent() | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 927 |         arg_tuple = ', '.join(repr(x.name) for x in node.args) | 
 | 928 |         if len(node.args) == 1: | 
 | 929 |             arg_tuple += ',' | 
| Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 930 |         self.writeline('caller = Macro(environment, call, None, (%s), (' % | 
 | 931 |                        arg_tuple) | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 932 |         for arg in node.defaults: | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 933 |             self.visit(arg, call_frame) | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 934 |             self.write(', ') | 
| Armin Ronacher | 963f97d | 2008-04-25 11:44:59 +0200 | [diff] [blame] | 935 |         self.write('), %s, %s, 0)' % ( | 
 | 936 |             call_frame.accesses_kwargs and '1' or '0', | 
 | 937 |             call_frame.accesses_varargs and '1' or '0' | 
 | 938 |         )) | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 939 |         if frame.buffer is None: | 
 | 940 |             self.writeline('yield ', node) | 
 | 941 |         else: | 
 | 942 |             self.writeline('%s.append(' % frame.buffer, node) | 
| Armin Ronacher | 2feed1d | 2008-04-26 16:26:52 +0200 | [diff] [blame] | 943 |         self.visit_Call(node.call, call_frame, | 
 | 944 |                         extra_kwargs={'caller': 'caller'}) | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 945 |         if frame.buffer is not None: | 
 | 946 |             self.write(')') | 
 | 947 |  | 
 | 948 |     def visit_FilterBlock(self, node, frame): | 
 | 949 |         filter_frame = frame.inner() | 
 | 950 |         filter_frame.inspect(node.iter_child_nodes()) | 
 | 951 |  | 
 | 952 |         aliases = self.collect_shadowed(filter_frame) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 953 |         self.pull_locals(filter_frame) | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 954 |         filter_frame.buffer = buf = self.temporary_identifier() | 
 | 955 |  | 
 | 956 |         self.writeline('%s = []' % buf, node) | 
 | 957 |         for child in node.body: | 
 | 958 |             self.visit(child, filter_frame) | 
 | 959 |  | 
 | 960 |         if frame.buffer is None: | 
 | 961 |             self.writeline('yield ', node) | 
 | 962 |         else: | 
 | 963 |             self.writeline('%s.append(' % frame.buffer, node) | 
| Armin Ronacher | de6bf71 | 2008-04-26 01:44:14 +0200 | [diff] [blame] | 964 |         self.visit_Filter(node.filter, filter_frame, 'concat(%s)' % buf) | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 965 |         if frame.buffer is not None: | 
 | 966 |             self.write(')') | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 967 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 968 |     def visit_ExprStmt(self, node, frame): | 
 | 969 |         self.newline(node) | 
| Armin Ronacher | 6ce170c | 2008-04-25 12:32:36 +0200 | [diff] [blame] | 970 |         self.visit(node.node, frame) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 971 |  | 
 | 972 |     def visit_Output(self, node, frame): | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 973 |         # if we have a known extends statement, we don't output anything | 
| Armin Ronacher | 7a52df8 | 2008-04-11 13:58:22 +0200 | [diff] [blame] | 974 |         if self.has_known_extends and frame.toplevel: | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 975 |             return | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 976 |  | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 977 |         self.newline(node) | 
| Armin Ronacher | 8edbe49 | 2008-04-10 20:43:43 +0200 | [diff] [blame] | 978 |  | 
| Armin Ronacher | 7fb3897 | 2008-04-11 13:54:28 +0200 | [diff] [blame] | 979 |         # if we are in the toplevel scope and there was already an extends | 
 | 980 |         # statement we have to add a check that disables our yield(s) here | 
 | 981 |         # so that they don't appear in the output. | 
 | 982 |         outdent_later = False | 
 | 983 |         if frame.toplevel and self.extends_so_far != 0: | 
| Armin Ronacher | 203bfcb | 2008-04-24 21:54:44 +0200 | [diff] [blame] | 984 |             self.writeline('if parent_template is None:') | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 985 |             self.indent() | 
| Armin Ronacher | 7fb3897 | 2008-04-11 13:54:28 +0200 | [diff] [blame] | 986 |             outdent_later = True | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 987 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 988 |         # try to evaluate as many chunks as possible into a static | 
 | 989 |         # string at compile time. | 
 | 990 |         body = [] | 
 | 991 |         for child in node.nodes: | 
 | 992 |             try: | 
 | 993 |                 const = unicode(child.as_const()) | 
 | 994 |             except: | 
 | 995 |                 body.append(child) | 
 | 996 |                 continue | 
 | 997 |             if body and isinstance(body[-1], list): | 
 | 998 |                 body[-1].append(const) | 
 | 999 |             else: | 
 | 1000 |                 body.append([const]) | 
 | 1001 |  | 
| Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 1002 |         # if we have less than 3 nodes or less than 6 and a buffer we | 
 | 1003 |         # yield or extend | 
 | 1004 |         if len(body) < 3 or (frame.buffer is not None and len(body) < 6): | 
 | 1005 |             if frame.buffer is not None: | 
 | 1006 |                 self.writeline('%s.extend((' % frame.buffer) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1007 |             for item in body: | 
 | 1008 |                 if isinstance(item, list): | 
| Armin Ronacher | de6bf71 | 2008-04-26 01:44:14 +0200 | [diff] [blame] | 1009 |                     val = repr(concat(item)) | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 1010 |                     if frame.buffer is None: | 
 | 1011 |                         self.writeline('yield ' + val) | 
 | 1012 |                     else: | 
| Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 1013 |                         self.write(val + ', ') | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1014 |                 else: | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 1015 |                     if frame.buffer is None: | 
| Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 1016 |                         self.writeline('yield ') | 
| Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame] | 1017 |                     close = 1 | 
 | 1018 |                     if self.environment.autoescape: | 
 | 1019 |                         self.write('escape(') | 
 | 1020 |                     else: | 
 | 1021 |                         self.write('unicode(') | 
 | 1022 |                     if self.environment.finalize is not None: | 
 | 1023 |                         self.write('environment.finalize(') | 
 | 1024 |                         close += 1 | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1025 |                     self.visit(item, frame) | 
| Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame] | 1026 |                     self.write(')' * close) | 
| Armin Ronacher | 32a910f | 2008-04-26 23:21:03 +0200 | [diff] [blame] | 1027 |                     if frame.buffer is not None: | 
 | 1028 |                         self.write(', ') | 
 | 1029 |             if frame.buffer is not None: | 
 | 1030 |                 self.write('))') | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1031 |  | 
 | 1032 |         # otherwise we create a format string as this is faster in that case | 
 | 1033 |         else: | 
 | 1034 |             format = [] | 
 | 1035 |             arguments = [] | 
 | 1036 |             for item in body: | 
 | 1037 |                 if isinstance(item, list): | 
| Armin Ronacher | de6bf71 | 2008-04-26 01:44:14 +0200 | [diff] [blame] | 1038 |                     format.append(concat(item).replace('%', '%%')) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1039 |                 else: | 
 | 1040 |                     format.append('%s') | 
 | 1041 |                     arguments.append(item) | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 1042 |             if frame.buffer is None: | 
 | 1043 |                 self.writeline('yield ') | 
 | 1044 |             else: | 
 | 1045 |                 self.writeline('%s.append(' % frame.buffer) | 
| Armin Ronacher | de6bf71 | 2008-04-26 01:44:14 +0200 | [diff] [blame] | 1046 |             self.write(repr(concat(format)) + ' % (') | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1047 |             idx = -1 | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 1048 |             self.indent() | 
 | 1049 |             for argument in arguments: | 
 | 1050 |                 self.newline(argument) | 
| Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame] | 1051 |                 close = 0 | 
 | 1052 |                 if self.environment.autoescape: | 
 | 1053 |                     self.write('escape(') | 
 | 1054 |                     close += 1 | 
 | 1055 |                 if self.environment.finalize is not None: | 
 | 1056 |                     self.write('environment.finalize(') | 
 | 1057 |                     close += 1 | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1058 |                 self.visit(argument, frame) | 
| Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame] | 1059 |                 self.write(')' * close + ',') | 
| Armin Ronacher | 8e8d071 | 2008-04-16 23:10:49 +0200 | [diff] [blame] | 1060 |             self.outdent() | 
 | 1061 |             self.writeline(')') | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 1062 |             if frame.buffer is not None: | 
 | 1063 |                 self.write(')') | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1064 |  | 
| Armin Ronacher | 7fb3897 | 2008-04-11 13:54:28 +0200 | [diff] [blame] | 1065 |         if outdent_later: | 
| Armin Ronacher | 75cfb86 | 2008-04-11 13:47:22 +0200 | [diff] [blame] | 1066 |             self.outdent() | 
 | 1067 |  | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1068 |     def visit_Assign(self, node, frame): | 
 | 1069 |         self.newline(node) | 
 | 1070 |         # toplevel assignments however go into the local namespace and | 
 | 1071 |         # the current template's context.  We create a copy of the frame | 
 | 1072 |         # here and add a set so that the Name visitor can add the assigned | 
 | 1073 |         # names here. | 
 | 1074 |         if frame.toplevel: | 
 | 1075 |             assignment_frame = frame.copy() | 
 | 1076 |             assignment_frame.assigned_names = set() | 
 | 1077 |         else: | 
 | 1078 |             assignment_frame = frame | 
 | 1079 |         self.visit(node.target, assignment_frame) | 
 | 1080 |         self.write(' = ') | 
 | 1081 |         self.visit(node.node, frame) | 
| Armin Ronacher | 9706fab | 2008-04-08 18:49:56 +0200 | [diff] [blame] | 1082 |  | 
 | 1083 |         # make sure toplevel assignments are added to the context. | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1084 |         if frame.toplevel: | 
 | 1085 |             for name in assignment_frame.assigned_names: | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 1086 |                 self.writeline('context.vars[%r] = l_%s' % (name, name)) | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 1087 |                 if not name.startswith('__'): | 
 | 1088 |                     self.writeline('context.exported_vars.add(%r)' % name) | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1089 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1090 |     def visit_Name(self, node, frame): | 
| Armin Ronacher | c9705c2 | 2008-04-27 21:28:03 +0200 | [diff] [blame] | 1091 |         if node.ctx == 'store' and frame.toplevel: | 
 | 1092 |             frame.assigned_names.add(node.name) | 
| Armin Ronacher | d1ff858 | 2008-05-11 00:30:43 +0200 | [diff] [blame] | 1093 |         self.write('l_' + node.name) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1094 |  | 
| Armin Ronacher | d84ec46 | 2008-04-29 13:43:16 +0200 | [diff] [blame] | 1095 |     def visit_MarkSafe(self, node, frame): | 
 | 1096 |         self.write('Markup(') | 
 | 1097 |         self.visit(node.expr, frame) | 
 | 1098 |         self.write(')') | 
 | 1099 |  | 
| Armin Ronacher | 023b5e9 | 2008-05-08 11:03:10 +0200 | [diff] [blame] | 1100 |     def visit_EnvironmentAttribute(self, node, frame): | 
 | 1101 |         self.write('environment.' + node.name) | 
 | 1102 |  | 
 | 1103 |     def visit_ExtensionAttribute(self, node, frame): | 
 | 1104 |         self.write('environment.extensions[%r].%s' % (node.identifier, node.attr)) | 
 | 1105 |  | 
 | 1106 |     def visit_ImportedName(self, node, frame): | 
 | 1107 |         self.write(self.import_aliases[node.importname]) | 
 | 1108 |  | 
 | 1109 |     def visit_InternalName(self, node, frame): | 
 | 1110 |         self.write(node.name) | 
 | 1111 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1112 |     def visit_Const(self, node, frame): | 
 | 1113 |         val = node.value | 
 | 1114 |         if isinstance(val, float): | 
 | 1115 |             # XXX: add checks for infinity and nan | 
 | 1116 |             self.write(str(val)) | 
 | 1117 |         else: | 
 | 1118 |             self.write(repr(val)) | 
 | 1119 |  | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1120 |     def visit_Tuple(self, node, frame): | 
 | 1121 |         self.write('(') | 
 | 1122 |         idx = -1 | 
 | 1123 |         for idx, item in enumerate(node.items): | 
 | 1124 |             if idx: | 
 | 1125 |                 self.write(', ') | 
 | 1126 |             self.visit(item, frame) | 
 | 1127 |         self.write(idx == 0 and ',)' or ')') | 
 | 1128 |  | 
| Armin Ronacher | 8edbe49 | 2008-04-10 20:43:43 +0200 | [diff] [blame] | 1129 |     def visit_List(self, node, frame): | 
 | 1130 |         self.write('[') | 
 | 1131 |         for idx, item in enumerate(node.items): | 
 | 1132 |             if idx: | 
 | 1133 |                 self.write(', ') | 
 | 1134 |             self.visit(item, frame) | 
 | 1135 |         self.write(']') | 
 | 1136 |  | 
 | 1137 |     def visit_Dict(self, node, frame): | 
 | 1138 |         self.write('{') | 
 | 1139 |         for idx, item in enumerate(node.items): | 
 | 1140 |             if idx: | 
 | 1141 |                 self.write(', ') | 
 | 1142 |             self.visit(item.key, frame) | 
 | 1143 |             self.write(': ') | 
 | 1144 |             self.visit(item.value, frame) | 
 | 1145 |         self.write('}') | 
 | 1146 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1147 |     def binop(operator): | 
 | 1148 |         def visitor(self, node, frame): | 
 | 1149 |             self.write('(') | 
 | 1150 |             self.visit(node.left, frame) | 
 | 1151 |             self.write(' %s ' % operator) | 
 | 1152 |             self.visit(node.right, frame) | 
 | 1153 |             self.write(')') | 
 | 1154 |         return visitor | 
 | 1155 |  | 
 | 1156 |     def uaop(operator): | 
 | 1157 |         def visitor(self, node, frame): | 
 | 1158 |             self.write('(' + operator) | 
| Armin Ronacher | 9a82205 | 2008-04-17 18:44:07 +0200 | [diff] [blame] | 1159 |             self.visit(node.node, frame) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1160 |             self.write(')') | 
 | 1161 |         return visitor | 
 | 1162 |  | 
 | 1163 |     visit_Add = binop('+') | 
 | 1164 |     visit_Sub = binop('-') | 
 | 1165 |     visit_Mul = binop('*') | 
 | 1166 |     visit_Div = binop('/') | 
 | 1167 |     visit_FloorDiv = binop('//') | 
 | 1168 |     visit_Pow = binop('**') | 
 | 1169 |     visit_Mod = binop('%') | 
 | 1170 |     visit_And = binop('and') | 
 | 1171 |     visit_Or = binop('or') | 
 | 1172 |     visit_Pos = uaop('+') | 
 | 1173 |     visit_Neg = uaop('-') | 
 | 1174 |     visit_Not = uaop('not ') | 
 | 1175 |     del binop, uaop | 
 | 1176 |  | 
| Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame] | 1177 |     def visit_Concat(self, node, frame): | 
| Armin Ronacher | 709f6e5 | 2008-04-28 18:18:16 +0200 | [diff] [blame] | 1178 |         self.write('%s((' % self.environment.autoescape and | 
 | 1179 |                    'markup_join' or 'unicode_join') | 
| Armin Ronacher | d134231 | 2008-04-28 12:20:12 +0200 | [diff] [blame] | 1180 |         for arg in node.nodes: | 
 | 1181 |             self.visit(arg, frame) | 
 | 1182 |             self.write(', ') | 
 | 1183 |         self.write('))') | 
 | 1184 |  | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1185 |     def visit_Compare(self, node, frame): | 
 | 1186 |         self.visit(node.expr, frame) | 
 | 1187 |         for op in node.ops: | 
 | 1188 |             self.visit(op, frame) | 
 | 1189 |  | 
 | 1190 |     def visit_Operand(self, node, frame): | 
 | 1191 |         self.write(' %s ' % operators[node.op]) | 
 | 1192 |         self.visit(node.expr, frame) | 
 | 1193 |  | 
 | 1194 |     def visit_Subscript(self, node, frame): | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1195 |         if isinstance(node.arg, nodes.Slice): | 
 | 1196 |             self.visit(node.node, frame) | 
 | 1197 |             self.write('[') | 
 | 1198 |             self.visit(node.arg, frame) | 
 | 1199 |             self.write(']') | 
 | 1200 |             return | 
 | 1201 |         try: | 
 | 1202 |             const = node.arg.as_const() | 
 | 1203 |             have_const = True | 
 | 1204 |         except nodes.Impossible: | 
 | 1205 |             have_const = False | 
| Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 1206 |         self.write('environment.subscribe(') | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1207 |         self.visit(node.node, frame) | 
 | 1208 |         self.write(', ') | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1209 |         if have_const: | 
 | 1210 |             self.write(repr(const)) | 
 | 1211 |         else: | 
 | 1212 |             self.visit(node.arg, frame) | 
| Armin Ronacher | 4dfc975 | 2008-04-09 15:03:29 +0200 | [diff] [blame] | 1213 |         self.write(')') | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1214 |  | 
 | 1215 |     def visit_Slice(self, node, frame): | 
 | 1216 |         if node.start is not None: | 
 | 1217 |             self.visit(node.start, frame) | 
 | 1218 |         self.write(':') | 
 | 1219 |         if node.stop is not None: | 
 | 1220 |             self.visit(node.stop, frame) | 
 | 1221 |         if node.step is not None: | 
 | 1222 |             self.write(':') | 
 | 1223 |             self.visit(node.step, frame) | 
 | 1224 |  | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 1225 |     def visit_Filter(self, node, frame, initial=None): | 
| Armin Ronacher | b9e7875 | 2008-05-10 23:36:28 +0200 | [diff] [blame] | 1226 |         self.write(self.filters[node.name] + '(') | 
| Christoph Hack | 8090986 | 2008-04-14 01:35:10 +0200 | [diff] [blame] | 1227 |         func = self.environment.filters.get(node.name) | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 1228 |         if func is None: | 
 | 1229 |             raise TemplateAssertionError('no filter named %r' % node.name, | 
 | 1230 |                                          node.lineno, self.filename) | 
| Christoph Hack | 8090986 | 2008-04-14 01:35:10 +0200 | [diff] [blame] | 1231 |         if getattr(func, 'contextfilter', False): | 
 | 1232 |             self.write('context, ') | 
| Armin Ronacher | 9a027f4 | 2008-04-17 11:13:40 +0200 | [diff] [blame] | 1233 |         elif getattr(func, 'environmentfilter', False): | 
 | 1234 |             self.write('environment, ') | 
| Christoph Hack | 8090986 | 2008-04-14 01:35:10 +0200 | [diff] [blame] | 1235 |         if isinstance(node.node, nodes.Filter): | 
 | 1236 |             self.visit_Filter(node.node, frame, initial) | 
 | 1237 |         elif node.node is None: | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 1238 |             self.write(initial) | 
 | 1239 |         else: | 
| Armin Ronacher | fa865fb | 2008-04-12 22:11:53 +0200 | [diff] [blame] | 1240 |             self.visit(node.node, frame) | 
| Armin Ronacher | d55ab53 | 2008-04-09 16:13:39 +0200 | [diff] [blame] | 1241 |         self.signature(node, frame) | 
 | 1242 |         self.write(')') | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1243 |  | 
 | 1244 |     def visit_Test(self, node, frame): | 
| Armin Ronacher | b9e7875 | 2008-05-10 23:36:28 +0200 | [diff] [blame] | 1245 |         self.write(self.tests[node.name] + '(') | 
| Armin Ronacher | 0611e49 | 2008-04-25 23:44:14 +0200 | [diff] [blame] | 1246 |         if node.name not in self.environment.tests: | 
 | 1247 |             raise TemplateAssertionError('no test named %r' % node.name, | 
 | 1248 |                                          node.lineno, self.filename) | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1249 |         self.visit(node.node, frame) | 
 | 1250 |         self.signature(node, frame) | 
| Armin Ronacher | e791c2a | 2008-04-07 18:39:54 +0200 | [diff] [blame] | 1251 |         self.write(')') | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1252 |  | 
| Armin Ronacher | 3d8b784 | 2008-04-13 13:16:50 +0200 | [diff] [blame] | 1253 |     def visit_CondExpr(self, node, frame): | 
 | 1254 |         if not have_condexpr: | 
 | 1255 |             self.write('((') | 
 | 1256 |             self.visit(node.test, frame) | 
 | 1257 |             self.write(') and (') | 
 | 1258 |             self.visit(node.expr1, frame) | 
 | 1259 |             self.write(',) or (') | 
 | 1260 |             self.visit(node.expr2, frame) | 
 | 1261 |             self.write(',))[0]') | 
 | 1262 |         else: | 
 | 1263 |             self.write('(') | 
 | 1264 |             self.visit(node.expr1, frame) | 
 | 1265 |             self.write(' if ') | 
 | 1266 |             self.visit(node.test, frame) | 
 | 1267 |             self.write(' else ') | 
 | 1268 |             self.visit(node.expr2, frame) | 
 | 1269 |             self.write(')') | 
 | 1270 |  | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 1271 |     def visit_Call(self, node, frame, extra_kwargs=None): | 
| Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 1272 |         if self.environment.sandboxed: | 
 | 1273 |             self.write('environment.call(') | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1274 |         self.visit(node.node, frame) | 
| Armin Ronacher | c63243e | 2008-04-14 22:53:58 +0200 | [diff] [blame] | 1275 |         self.write(self.environment.sandboxed and ', ' or '(') | 
| Armin Ronacher | 7108207 | 2008-04-12 14:19:36 +0200 | [diff] [blame] | 1276 |         self.signature(node, frame, False, extra_kwargs) | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1277 |         self.write(')') | 
 | 1278 |  | 
 | 1279 |     def visit_Keyword(self, node, frame): | 
| Armin Ronacher | 2e9396b | 2008-04-16 14:21:57 +0200 | [diff] [blame] | 1280 |         self.write(node.key + '=') | 
| Armin Ronacher | 8efc522 | 2008-04-08 14:47:40 +0200 | [diff] [blame] | 1281 |         self.visit(node.value, frame) |