|  | # -*- coding: utf-8 -*- | 
|  | """ | 
|  | jinja2.meta | 
|  | ~~~~~~~~~~~ | 
|  |  | 
|  | This module implements various functions that exposes information about | 
|  | templates that might be interesting for various kinds of applications. | 
|  |  | 
|  | :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details. | 
|  | :license: BSD, see LICENSE for more details. | 
|  | """ | 
|  | from jinja2 import nodes | 
|  | from jinja2.compiler import CodeGenerator | 
|  | from jinja2._compat import string_types | 
|  |  | 
|  |  | 
|  | class TrackingCodeGenerator(CodeGenerator): | 
|  | """We abuse the code generator for introspection.""" | 
|  |  | 
|  | def __init__(self, environment): | 
|  | CodeGenerator.__init__(self, environment, '<introspection>', | 
|  | '<introspection>') | 
|  | self.undeclared_identifiers = set() | 
|  |  | 
|  | def write(self, x): | 
|  | """Don't write.""" | 
|  |  | 
|  | def pull_locals(self, frame): | 
|  | """Remember all undeclared identifiers.""" | 
|  | self.undeclared_identifiers.update(frame.identifiers.undeclared) | 
|  |  | 
|  |  | 
|  | def find_undeclared_variables(ast): | 
|  | """Returns a set of all variables in the AST that will be looked up from | 
|  | the context at runtime.  Because at compile time it's not known which | 
|  | variables will be used depending on the path the execution takes at | 
|  | runtime, all variables are returned. | 
|  |  | 
|  | >>> from jinja2 import Environment, meta | 
|  | >>> env = Environment() | 
|  | >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') | 
|  | >>> meta.find_undeclared_variables(ast) | 
|  | set(['bar']) | 
|  |  | 
|  | .. admonition:: Implementation | 
|  |  | 
|  | Internally the code generator is used for finding undeclared variables. | 
|  | This is good to know because the code generator might raise a | 
|  | :exc:`TemplateAssertionError` during compilation and as a matter of | 
|  | fact this function can currently raise that exception as well. | 
|  | """ | 
|  | codegen = TrackingCodeGenerator(ast.environment) | 
|  | codegen.visit(ast) | 
|  | return codegen.undeclared_identifiers | 
|  |  | 
|  |  | 
|  | def find_referenced_templates(ast): | 
|  | """Finds all the referenced templates from the AST.  This will return an | 
|  | iterator over all the hardcoded template extensions, inclusions and | 
|  | imports.  If dynamic inheritance or inclusion is used, `None` will be | 
|  | yielded. | 
|  |  | 
|  | >>> from jinja2 import Environment, meta | 
|  | >>> env = Environment() | 
|  | >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') | 
|  | >>> list(meta.find_referenced_templates(ast)) | 
|  | ['layout.html', None] | 
|  |  | 
|  | This function is useful for dependency tracking.  For example if you want | 
|  | to rebuild parts of the website after a layout template has changed. | 
|  | """ | 
|  | for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, | 
|  | nodes.Include)): | 
|  | if not isinstance(node.template, nodes.Const): | 
|  | # a tuple with some non consts in there | 
|  | if isinstance(node.template, (nodes.Tuple, nodes.List)): | 
|  | for template_name in node.template.items: | 
|  | # something const, only yield the strings and ignore | 
|  | # non-string consts that really just make no sense | 
|  | if isinstance(template_name, nodes.Const): | 
|  | if isinstance(template_name.value, string_types): | 
|  | yield template_name.value | 
|  | # something dynamic in there | 
|  | else: | 
|  | yield None | 
|  | # something dynamic we don't know about here | 
|  | else: | 
|  | yield None | 
|  | continue | 
|  | # constant is a basestring, direct template name | 
|  | if isinstance(node.template.value, string_types): | 
|  | yield node.template.value | 
|  | # a tuple or list (latter *should* not happen) made of consts, | 
|  | # yield the consts that are strings.  We could warn here for | 
|  | # non string values | 
|  | elif isinstance(node, nodes.Include) and \ | 
|  | isinstance(node.template.value, (tuple, list)): | 
|  | for template_name in node.template.value: | 
|  | if isinstance(template_name, string_types): | 
|  | yield template_name | 
|  | # something else we don't care about, we could warn here | 
|  | else: | 
|  | yield None |