meta.py revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
1b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)# -*- coding: utf-8 -*- 2b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)""" 3b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) jinja2.meta 4b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ~~~~~~~~~~~ 5b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 6b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) This module implements various functions that exposes information about 7b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) templates that might be interesting for various kinds of applications. 8b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 9b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details. 10b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) :license: BSD, see LICENSE for more details. 11b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)""" 12b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from jinja2 import nodes 13b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from jinja2.compiler import CodeGenerator 14b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 15b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 16b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)class TrackingCodeGenerator(CodeGenerator): 17b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """We abuse the code generator for introspection.""" 18b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 19b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def __init__(self, environment): 20b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) CodeGenerator.__init__(self, environment, '<introspection>', 21b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) '<introspection>') 22b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.undeclared_identifiers = set() 23b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 24b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def write(self, x): 25b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Don't write.""" 26b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 27b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) def pull_locals(self, frame): 28b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Remember all undeclared identifiers.""" 29b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) self.undeclared_identifiers.update(frame.identifiers.undeclared) 30b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 31b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 32b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)def find_undeclared_variables(ast): 33b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Returns a set of all variables in the AST that will be looked up from 34b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) the context at runtime. Because at compile time it's not known which 35b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) variables will be used depending on the path the execution takes at 36b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) runtime, all variables are returned. 37b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 38b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> from jinja2 import Environment, meta 39b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> env = Environment() 40b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') 41b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> meta.find_undeclared_variables(ast) 42b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) set(['bar']) 43b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 44b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) .. admonition:: Implementation 45b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 46b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) Internally the code generator is used for finding undeclared variables. 47b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) This is good to know because the code generator might raise a 48b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) :exc:`TemplateAssertionError` during compilation and as a matter of 49b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) fact this function can currently raise that exception as well. 50b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 51b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) codegen = TrackingCodeGenerator(ast.environment) 52b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) codegen.visit(ast) 53b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) return codegen.undeclared_identifiers 54b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 55b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 56b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)def find_referenced_templates(ast): 57b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """Finds all the referenced templates from the AST. This will return an 58b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) iterator over all the hardcoded template extensions, inclusions and 59b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) imports. If dynamic inheritance or inclusion is used, `None` will be 60b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) yielded. 61b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 62b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> from jinja2 import Environment, meta 63b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> env = Environment() 64b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') 65b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) >>> list(meta.find_referenced_templates(ast)) 66b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) ['layout.html', None] 67b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) 68b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) This function is useful for dependency tracking. For example if you want 69b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) to rebuild parts of the website after a layout template has changed. 70b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) """ 71b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, 72b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) nodes.Include)): 73b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if not isinstance(node.template, nodes.Const): 74b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # a tuple with some non consts in there 75b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if isinstance(node.template, (nodes.Tuple, nodes.List)): 76b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for template_name in node.template.items: 77b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # something const, only yield the strings and ignore 78b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # non-string consts that really just make no sense 79b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if isinstance(template_name, nodes.Const): 80b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if isinstance(template_name.value, basestring): 81b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) yield template_name.value 82b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # something dynamic in there 83b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) else: 84b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) yield None 85b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # something dynamic we don't know about here 86b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) else: 87b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) yield None 88b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) continue 89b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # constant is a basestring, direct template name 90b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if isinstance(node.template.value, basestring): 91b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) yield node.template.value 92b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # a tuple or list (latter *should* not happen) made of consts, 93b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # yield the consts that are strings. We could warn here for 94b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # non string values 95b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) elif isinstance(node, nodes.Include) and \ 96b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) isinstance(node.template.value, (tuple, list)): 97b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) for template_name in node.template.value: 98b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if isinstance(template_name, basestring): 99b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) yield template_name 100b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) # something else we don't care about, we could warn here 101b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) else: 102b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) yield None 103