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