| OLD | NEW |
| 1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
| 2 """ | 2 """ |
| 3 jinja2.compiler | 3 jinja2.compiler |
| 4 ~~~~~~~~~~~~~~~ | 4 ~~~~~~~~~~~~~~~ |
| 5 | 5 |
| 6 Compiles nodes into python code. | 6 Compiles nodes into python code. |
| 7 | 7 |
| 8 :copyright: (c) 2010 by the Jinja Team. | 8 :copyright: (c) 2010 by the Jinja Team. |
| 9 :license: BSD, see LICENSE for more details. | 9 :license: BSD, see LICENSE for more details. |
| 10 """ | 10 """ |
| 11 from itertools import chain | 11 from itertools import chain |
| 12 from copy import deepcopy | 12 from copy import deepcopy |
| 13 from keyword import iskeyword as is_python_keyword | 13 from keyword import iskeyword as is_python_keyword |
| 14 from jinja2 import nodes | 14 from jinja2 import nodes |
| 15 from jinja2.nodes import EvalContext | 15 from jinja2.nodes import EvalContext |
| 16 from jinja2.visitor import NodeVisitor | 16 from jinja2.visitor import NodeVisitor |
| 17 from jinja2.exceptions import TemplateAssertionError | 17 from jinja2.exceptions import TemplateAssertionError |
| 18 from jinja2.utils import Markup, concat, escape | 18 from jinja2.utils import Markup, concat, escape |
| 19 from jinja2._compat import range_type, next, text_type, string_types, \ | 19 from jinja2._compat import range_type, text_type, string_types, \ |
| 20 iteritems, NativeStringIO, imap | 20 iteritems, NativeStringIO, imap |
| 21 | 21 |
| 22 | 22 |
| 23 operators = { | 23 operators = { |
| 24 'eq': '==', | 24 'eq': '==', |
| 25 'ne': '!=', | 25 'ne': '!=', |
| 26 'gt': '>', | 26 'gt': '>', |
| 27 'gteq': '>=', | 27 'gteq': '>=', |
| 28 'lt': '<', | 28 'lt': '<', |
| 29 'lteq': '<=', | 29 'lteq': '<=', |
| (...skipping 20 matching lines...) Expand all Loading... |
| 50 # no closure is on the function | 50 # no closure is on the function |
| 51 unoptimize_before_dead_code = bool( | 51 unoptimize_before_dead_code = bool( |
| 52 getattr(unoptimize_before_dead_code(), '__closure__', None)) | 52 getattr(unoptimize_before_dead_code(), '__closure__', None)) |
| 53 | 53 |
| 54 | 54 |
| 55 def generate(node, environment, name, filename, stream=None, | 55 def generate(node, environment, name, filename, stream=None, |
| 56 defer_init=False): | 56 defer_init=False): |
| 57 """Generate the python source for a node tree.""" | 57 """Generate the python source for a node tree.""" |
| 58 if not isinstance(node, nodes.Template): | 58 if not isinstance(node, nodes.Template): |
| 59 raise TypeError('Can\'t compile non template nodes') | 59 raise TypeError('Can\'t compile non template nodes') |
| 60 generator = CodeGenerator(environment, name, filename, stream, defer_init) | 60 generator = environment.code_generator_class(environment, name, filename, |
| 61 stream, defer_init) |
| 61 generator.visit(node) | 62 generator.visit(node) |
| 62 if stream is None: | 63 if stream is None: |
| 63 return generator.stream.getvalue() | 64 return generator.stream.getvalue() |
| 64 | 65 |
| 65 | 66 |
| 66 def has_safe_repr(value): | 67 def has_safe_repr(value): |
| 67 """Does the node have a safe representation?""" | 68 """Does the node have a safe representation?""" |
| 68 if value is None or value is NotImplemented or value is Ellipsis: | 69 if value is None or value is NotImplemented or value is Ellipsis: |
| 69 return True | 70 return True |
| 70 if isinstance(value, (bool, int, float, complex, range_type, | 71 if isinstance(value, (bool, int, float, complex, range_type, |
| (...skipping 269 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 is visited as part of the outer scope. | 341 is visited as part of the outer scope. |
| 341 """ | 342 """ |
| 342 self.visit(node.iter) | 343 self.visit(node.iter) |
| 343 | 344 |
| 344 def visit_CallBlock(self, node): | 345 def visit_CallBlock(self, node): |
| 345 self.visit(node.call) | 346 self.visit(node.call) |
| 346 | 347 |
| 347 def visit_FilterBlock(self, node): | 348 def visit_FilterBlock(self, node): |
| 348 self.visit(node.filter) | 349 self.visit(node.filter) |
| 349 | 350 |
| 351 def visit_AssignBlock(self, node): |
| 352 """Stop visiting at block assigns.""" |
| 353 |
| 350 def visit_Scope(self, node): | 354 def visit_Scope(self, node): |
| 351 """Stop visiting at scopes.""" | 355 """Stop visiting at scopes.""" |
| 352 | 356 |
| 353 def visit_Block(self, node): | 357 def visit_Block(self, node): |
| 354 """Stop visiting at blocks.""" | 358 """Stop visiting at blocks.""" |
| 355 | 359 |
| 356 | 360 |
| 357 class CompilerExit(Exception): | 361 class CompilerExit(Exception): |
| 358 """Raised if the compiler encountered a situation where it just | 362 """Raised if the compiler encountered a situation where it just |
| 359 doesn't make sense to further process the code. Any block that | 363 doesn't make sense to further process the code. Any block that |
| (...skipping 848 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1208 def visit_ExprStmt(self, node, frame): | 1212 def visit_ExprStmt(self, node, frame): |
| 1209 self.newline(node) | 1213 self.newline(node) |
| 1210 self.visit(node.node, frame) | 1214 self.visit(node.node, frame) |
| 1211 | 1215 |
| 1212 def visit_Output(self, node, frame): | 1216 def visit_Output(self, node, frame): |
| 1213 # if we have a known extends statement, we don't output anything | 1217 # if we have a known extends statement, we don't output anything |
| 1214 # if we are in a require_output_check section | 1218 # if we are in a require_output_check section |
| 1215 if self.has_known_extends and frame.require_output_check: | 1219 if self.has_known_extends and frame.require_output_check: |
| 1216 return | 1220 return |
| 1217 | 1221 |
| 1222 allow_constant_finalize = True |
| 1218 if self.environment.finalize: | 1223 if self.environment.finalize: |
| 1219 finalize = lambda x: text_type(self.environment.finalize(x)) | 1224 func = self.environment.finalize |
| 1225 if getattr(func, 'contextfunction', False) or \ |
| 1226 getattr(func, 'evalcontextfunction', False): |
| 1227 allow_constant_finalize = False |
| 1228 elif getattr(func, 'environmentfunction', False): |
| 1229 finalize = lambda x: text_type( |
| 1230 self.environment.finalize(self.environment, x)) |
| 1231 else: |
| 1232 finalize = lambda x: text_type(self.environment.finalize(x)) |
| 1220 else: | 1233 else: |
| 1221 finalize = text_type | 1234 finalize = text_type |
| 1222 | 1235 |
| 1223 # if we are inside a frame that requires output checking, we do so | 1236 # if we are inside a frame that requires output checking, we do so |
| 1224 outdent_later = False | 1237 outdent_later = False |
| 1225 if frame.require_output_check: | 1238 if frame.require_output_check: |
| 1226 self.writeline('if parent_template is None:') | 1239 self.writeline('if parent_template is None:') |
| 1227 self.indent() | 1240 self.indent() |
| 1228 outdent_later = True | 1241 outdent_later = True |
| 1229 | 1242 |
| 1230 # try to evaluate as many chunks as possible into a static | 1243 # try to evaluate as many chunks as possible into a static |
| 1231 # string at compile time. | 1244 # string at compile time. |
| 1232 body = [] | 1245 body = [] |
| 1233 for child in node.nodes: | 1246 for child in node.nodes: |
| 1234 try: | 1247 try: |
| 1248 if not allow_constant_finalize: |
| 1249 raise nodes.Impossible() |
| 1235 const = child.as_const(frame.eval_ctx) | 1250 const = child.as_const(frame.eval_ctx) |
| 1236 except nodes.Impossible: | 1251 except nodes.Impossible: |
| 1237 body.append(child) | 1252 body.append(child) |
| 1238 continue | 1253 continue |
| 1239 # the frame can't be volatile here, becaus otherwise the | 1254 # the frame can't be volatile here, becaus otherwise the |
| 1240 # as_const() function would raise an Impossible exception | 1255 # as_const() function would raise an Impossible exception |
| 1241 # at that point. | 1256 # at that point. |
| 1242 try: | 1257 try: |
| 1243 if frame.eval_ctx.autoescape: | 1258 if frame.eval_ctx.autoescape: |
| 1244 if hasattr(const, '__html__'): | 1259 if hasattr(const, '__html__'): |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1280 close = 1 | 1295 close = 1 |
| 1281 if frame.eval_ctx.volatile: | 1296 if frame.eval_ctx.volatile: |
| 1282 self.write('(context.eval_ctx.autoescape and' | 1297 self.write('(context.eval_ctx.autoescape and' |
| 1283 ' escape or to_string)(') | 1298 ' escape or to_string)(') |
| 1284 elif frame.eval_ctx.autoescape: | 1299 elif frame.eval_ctx.autoescape: |
| 1285 self.write('escape(') | 1300 self.write('escape(') |
| 1286 else: | 1301 else: |
| 1287 self.write('to_string(') | 1302 self.write('to_string(') |
| 1288 if self.environment.finalize is not None: | 1303 if self.environment.finalize is not None: |
| 1289 self.write('environment.finalize(') | 1304 self.write('environment.finalize(') |
| 1305 if getattr(self.environment.finalize, |
| 1306 "contextfunction", False): |
| 1307 self.write('context, ') |
| 1290 close += 1 | 1308 close += 1 |
| 1291 self.visit(item, frame) | 1309 self.visit(item, frame) |
| 1292 self.write(')' * close) | 1310 self.write(')' * close) |
| 1293 if frame.buffer is not None: | 1311 if frame.buffer is not None: |
| 1294 self.write(', ') | 1312 self.write(', ') |
| 1295 if frame.buffer is not None: | 1313 if frame.buffer is not None: |
| 1296 # close the open parentheses | 1314 # close the open parentheses |
| 1297 self.outdent() | 1315 self.outdent() |
| 1298 self.writeline(len(body) == 1 and ')' or '))') | 1316 self.writeline(len(body) == 1 and ')' or '))') |
| 1299 | 1317 |
| 1300 # otherwise we create a format string as this is faster in that case | 1318 # otherwise we create a format string as this is faster in that case |
| 1301 else: | 1319 else: |
| 1302 format = [] | 1320 format = [] |
| 1303 arguments = [] | 1321 arguments = [] |
| 1304 for item in body: | 1322 for item in body: |
| 1305 if isinstance(item, list): | 1323 if isinstance(item, list): |
| 1306 format.append(concat(item).replace('%', '%%')) | 1324 format.append(concat(item).replace('%', '%%')) |
| 1307 else: | 1325 else: |
| 1308 format.append('%s') | 1326 format.append('%s') |
| 1309 arguments.append(item) | 1327 arguments.append(item) |
| 1310 self.writeline('yield ') | 1328 self.writeline('yield ') |
| 1311 self.write(repr(concat(format)) + ' % (') | 1329 self.write(repr(concat(format)) + ' % (') |
| 1312 idx = -1 | |
| 1313 self.indent() | 1330 self.indent() |
| 1314 for argument in arguments: | 1331 for argument in arguments: |
| 1315 self.newline(argument) | 1332 self.newline(argument) |
| 1316 close = 0 | 1333 close = 0 |
| 1317 if frame.eval_ctx.volatile: | 1334 if frame.eval_ctx.volatile: |
| 1318 self.write('(context.eval_ctx.autoescape and' | 1335 self.write('(context.eval_ctx.autoescape and' |
| 1319 ' escape or to_string)(') | 1336 ' escape or to_string)(') |
| 1320 close += 1 | 1337 close += 1 |
| 1321 elif frame.eval_ctx.autoescape: | 1338 elif frame.eval_ctx.autoescape: |
| 1322 self.write('escape(') | 1339 self.write('escape(') |
| 1323 close += 1 | 1340 close += 1 |
| 1324 if self.environment.finalize is not None: | 1341 if self.environment.finalize is not None: |
| 1325 self.write('environment.finalize(') | 1342 self.write('environment.finalize(') |
| 1343 if getattr(self.environment.finalize, |
| 1344 'contextfunction', False): |
| 1345 self.write('context, ') |
| 1346 elif getattr(self.environment.finalize, |
| 1347 'evalcontextfunction', False): |
| 1348 self.write('context.eval_ctx, ') |
| 1349 elif getattr(self.environment.finalize, |
| 1350 'environmentfunction', False): |
| 1351 self.write('environment, ') |
| 1326 close += 1 | 1352 close += 1 |
| 1327 self.visit(argument, frame) | 1353 self.visit(argument, frame) |
| 1328 self.write(')' * close + ', ') | 1354 self.write(')' * close + ', ') |
| 1329 self.outdent() | 1355 self.outdent() |
| 1330 self.writeline(')') | 1356 self.writeline(')') |
| 1331 | 1357 |
| 1332 if outdent_later: | 1358 if outdent_later: |
| 1333 self.outdent() | 1359 self.outdent() |
| 1334 | 1360 |
| 1335 def visit_Assign(self, node, frame): | 1361 def make_assignment_frame(self, frame): |
| 1336 self.newline(node) | |
| 1337 # toplevel assignments however go into the local namespace and | 1362 # toplevel assignments however go into the local namespace and |
| 1338 # the current template's context. We create a copy of the frame | 1363 # the current template's context. We create a copy of the frame |
| 1339 # here and add a set so that the Name visitor can add the assigned | 1364 # here and add a set so that the Name visitor can add the assigned |
| 1340 # names here. | 1365 # names here. |
| 1341 if frame.toplevel: | 1366 if not frame.toplevel: |
| 1342 assignment_frame = frame.copy() | 1367 return frame |
| 1343 assignment_frame.toplevel_assignments = set() | 1368 assignment_frame = frame.copy() |
| 1369 assignment_frame.toplevel_assignments = set() |
| 1370 return assignment_frame |
| 1371 |
| 1372 def export_assigned_vars(self, frame, assignment_frame): |
| 1373 if not frame.toplevel: |
| 1374 return |
| 1375 public_names = [x for x in assignment_frame.toplevel_assignments |
| 1376 if not x.startswith('_')] |
| 1377 if len(assignment_frame.toplevel_assignments) == 1: |
| 1378 name = next(iter(assignment_frame.toplevel_assignments)) |
| 1379 self.writeline('context.vars[%r] = l_%s' % (name, name)) |
| 1344 else: | 1380 else: |
| 1345 assignment_frame = frame | 1381 self.writeline('context.vars.update({') |
| 1382 for idx, name in enumerate(assignment_frame.toplevel_assignments): |
| 1383 if idx: |
| 1384 self.write(', ') |
| 1385 self.write('%r: l_%s' % (name, name)) |
| 1386 self.write('})') |
| 1387 if public_names: |
| 1388 if len(public_names) == 1: |
| 1389 self.writeline('context.exported_vars.add(%r)' % |
| 1390 public_names[0]) |
| 1391 else: |
| 1392 self.writeline('context.exported_vars.update((%s))' % |
| 1393 ', '.join(imap(repr, public_names))) |
| 1394 |
| 1395 def visit_Assign(self, node, frame): |
| 1396 self.newline(node) |
| 1397 assignment_frame = self.make_assignment_frame(frame) |
| 1346 self.visit(node.target, assignment_frame) | 1398 self.visit(node.target, assignment_frame) |
| 1347 self.write(' = ') | 1399 self.write(' = ') |
| 1348 self.visit(node.node, frame) | 1400 self.visit(node.node, frame) |
| 1401 self.export_assigned_vars(frame, assignment_frame) |
| 1349 | 1402 |
| 1350 # make sure toplevel assignments are added to the context. | 1403 def visit_AssignBlock(self, node, frame): |
| 1351 if frame.toplevel: | 1404 block_frame = frame.inner() |
| 1352 public_names = [x for x in assignment_frame.toplevel_assignments | 1405 block_frame.inspect(node.body) |
| 1353 if not x.startswith('_')] | 1406 aliases = self.push_scope(block_frame) |
| 1354 if len(assignment_frame.toplevel_assignments) == 1: | 1407 self.pull_locals(block_frame) |
| 1355 name = next(iter(assignment_frame.toplevel_assignments)) | 1408 self.buffer(block_frame) |
| 1356 self.writeline('context.vars[%r] = l_%s' % (name, name)) | 1409 self.blockvisit(node.body, block_frame) |
| 1357 else: | 1410 self.pop_scope(aliases, block_frame) |
| 1358 self.writeline('context.vars.update({') | 1411 |
| 1359 for idx, name in enumerate(assignment_frame.toplevel_assignments
): | 1412 assignment_frame = self.make_assignment_frame(frame) |
| 1360 if idx: | 1413 self.newline(node) |
| 1361 self.write(', ') | 1414 self.visit(node.target, assignment_frame) |
| 1362 self.write('%r: l_%s' % (name, name)) | 1415 self.write(' = concat(%s)' % block_frame.buffer) |
| 1363 self.write('})') | 1416 self.export_assigned_vars(frame, assignment_frame) |
| 1364 if public_names: | |
| 1365 if len(public_names) == 1: | |
| 1366 self.writeline('context.exported_vars.add(%r)' % | |
| 1367 public_names[0]) | |
| 1368 else: | |
| 1369 self.writeline('context.exported_vars.update((%s))' % | |
| 1370 ', '.join(imap(repr, public_names))) | |
| 1371 | 1417 |
| 1372 # -- Expression Visitors | 1418 # -- Expression Visitors |
| 1373 | 1419 |
| 1374 def visit_Name(self, node, frame): | 1420 def visit_Name(self, node, frame): |
| 1375 if node.ctx == 'store' and frame.toplevel: | 1421 if node.ctx == 'store' and frame.toplevel: |
| 1376 frame.toplevel_assignments.add(node.name) | 1422 frame.toplevel_assignments.add(node.name) |
| 1377 self.write('l_' + node.name) | 1423 self.write('l_' + node.name) |
| 1378 frame.assigned_names.add(node.name) | 1424 frame.assigned_names.add(node.name) |
| 1379 | 1425 |
| 1380 def visit_Const(self, node, frame): | 1426 def visit_Const(self, node, frame): |
| (...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1631 | 1677 |
| 1632 def visit_ScopedEvalContextModifier(self, node, frame): | 1678 def visit_ScopedEvalContextModifier(self, node, frame): |
| 1633 old_ctx_name = self.temporary_identifier() | 1679 old_ctx_name = self.temporary_identifier() |
| 1634 safed_ctx = frame.eval_ctx.save() | 1680 safed_ctx = frame.eval_ctx.save() |
| 1635 self.writeline('%s = context.eval_ctx.save()' % old_ctx_name) | 1681 self.writeline('%s = context.eval_ctx.save()' % old_ctx_name) |
| 1636 self.visit_EvalContextModifier(node, frame) | 1682 self.visit_EvalContextModifier(node, frame) |
| 1637 for child in node.body: | 1683 for child in node.body: |
| 1638 self.visit(child, frame) | 1684 self.visit(child, frame) |
| 1639 frame.eval_ctx.revert(safed_ctx) | 1685 frame.eval_ctx.revert(safed_ctx) |
| 1640 self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name) | 1686 self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name) |
| OLD | NEW |