| # -*- 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 |