11cb0ef41Sopenharmony_ci# -*- coding: utf-8 -*- 21cb0ef41Sopenharmony_ci""" 31cb0ef41Sopenharmony_ci jinja2.meta 41cb0ef41Sopenharmony_ci ~~~~~~~~~~~ 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ci This module implements various functions that exposes information about 71cb0ef41Sopenharmony_ci templates that might be interesting for various kinds of applications. 81cb0ef41Sopenharmony_ci 91cb0ef41Sopenharmony_ci :copyright: (c) 2017 by the Jinja Team, see AUTHORS for more details. 101cb0ef41Sopenharmony_ci :license: BSD, see LICENSE for more details. 111cb0ef41Sopenharmony_ci""" 121cb0ef41Sopenharmony_cifrom jinja2 import nodes 131cb0ef41Sopenharmony_cifrom jinja2.compiler import CodeGenerator 141cb0ef41Sopenharmony_cifrom jinja2._compat import string_types, iteritems 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_ci 171cb0ef41Sopenharmony_ciclass TrackingCodeGenerator(CodeGenerator): 181cb0ef41Sopenharmony_ci """We abuse the code generator for introspection.""" 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ci def __init__(self, environment): 211cb0ef41Sopenharmony_ci CodeGenerator.__init__(self, environment, '<introspection>', 221cb0ef41Sopenharmony_ci '<introspection>') 231cb0ef41Sopenharmony_ci self.undeclared_identifiers = set() 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ci def write(self, x): 261cb0ef41Sopenharmony_ci """Don't write.""" 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ci def enter_frame(self, frame): 291cb0ef41Sopenharmony_ci """Remember all undeclared identifiers.""" 301cb0ef41Sopenharmony_ci CodeGenerator.enter_frame(self, frame) 311cb0ef41Sopenharmony_ci for _, (action, param) in iteritems(frame.symbols.loads): 321cb0ef41Sopenharmony_ci if action == 'resolve': 331cb0ef41Sopenharmony_ci self.undeclared_identifiers.add(param) 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ci 361cb0ef41Sopenharmony_cidef find_undeclared_variables(ast): 371cb0ef41Sopenharmony_ci """Returns a set of all variables in the AST that will be looked up from 381cb0ef41Sopenharmony_ci the context at runtime. Because at compile time it's not known which 391cb0ef41Sopenharmony_ci variables will be used depending on the path the execution takes at 401cb0ef41Sopenharmony_ci runtime, all variables are returned. 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci >>> from jinja2 import Environment, meta 431cb0ef41Sopenharmony_ci >>> env = Environment() 441cb0ef41Sopenharmony_ci >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') 451cb0ef41Sopenharmony_ci >>> meta.find_undeclared_variables(ast) == set(['bar']) 461cb0ef41Sopenharmony_ci True 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci .. admonition:: Implementation 491cb0ef41Sopenharmony_ci 501cb0ef41Sopenharmony_ci Internally the code generator is used for finding undeclared variables. 511cb0ef41Sopenharmony_ci This is good to know because the code generator might raise a 521cb0ef41Sopenharmony_ci :exc:`TemplateAssertionError` during compilation and as a matter of 531cb0ef41Sopenharmony_ci fact this function can currently raise that exception as well. 541cb0ef41Sopenharmony_ci """ 551cb0ef41Sopenharmony_ci codegen = TrackingCodeGenerator(ast.environment) 561cb0ef41Sopenharmony_ci codegen.visit(ast) 571cb0ef41Sopenharmony_ci return codegen.undeclared_identifiers 581cb0ef41Sopenharmony_ci 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_cidef find_referenced_templates(ast): 611cb0ef41Sopenharmony_ci """Finds all the referenced templates from the AST. This will return an 621cb0ef41Sopenharmony_ci iterator over all the hardcoded template extensions, inclusions and 631cb0ef41Sopenharmony_ci imports. If dynamic inheritance or inclusion is used, `None` will be 641cb0ef41Sopenharmony_ci yielded. 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci >>> from jinja2 import Environment, meta 671cb0ef41Sopenharmony_ci >>> env = Environment() 681cb0ef41Sopenharmony_ci >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') 691cb0ef41Sopenharmony_ci >>> list(meta.find_referenced_templates(ast)) 701cb0ef41Sopenharmony_ci ['layout.html', None] 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci This function is useful for dependency tracking. For example if you want 731cb0ef41Sopenharmony_ci to rebuild parts of the website after a layout template has changed. 741cb0ef41Sopenharmony_ci """ 751cb0ef41Sopenharmony_ci for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, 761cb0ef41Sopenharmony_ci nodes.Include)): 771cb0ef41Sopenharmony_ci if not isinstance(node.template, nodes.Const): 781cb0ef41Sopenharmony_ci # a tuple with some non consts in there 791cb0ef41Sopenharmony_ci if isinstance(node.template, (nodes.Tuple, nodes.List)): 801cb0ef41Sopenharmony_ci for template_name in node.template.items: 811cb0ef41Sopenharmony_ci # something const, only yield the strings and ignore 821cb0ef41Sopenharmony_ci # non-string consts that really just make no sense 831cb0ef41Sopenharmony_ci if isinstance(template_name, nodes.Const): 841cb0ef41Sopenharmony_ci if isinstance(template_name.value, string_types): 851cb0ef41Sopenharmony_ci yield template_name.value 861cb0ef41Sopenharmony_ci # something dynamic in there 871cb0ef41Sopenharmony_ci else: 881cb0ef41Sopenharmony_ci yield None 891cb0ef41Sopenharmony_ci # something dynamic we don't know about here 901cb0ef41Sopenharmony_ci else: 911cb0ef41Sopenharmony_ci yield None 921cb0ef41Sopenharmony_ci continue 931cb0ef41Sopenharmony_ci # constant is a basestring, direct template name 941cb0ef41Sopenharmony_ci if isinstance(node.template.value, string_types): 951cb0ef41Sopenharmony_ci yield node.template.value 961cb0ef41Sopenharmony_ci # a tuple or list (latter *should* not happen) made of consts, 971cb0ef41Sopenharmony_ci # yield the consts that are strings. We could warn here for 981cb0ef41Sopenharmony_ci # non string values 991cb0ef41Sopenharmony_ci elif isinstance(node, nodes.Include) and \ 1001cb0ef41Sopenharmony_ci isinstance(node.template.value, (tuple, list)): 1011cb0ef41Sopenharmony_ci for template_name in node.template.value: 1021cb0ef41Sopenharmony_ci if isinstance(template_name, string_types): 1031cb0ef41Sopenharmony_ci yield template_name 1041cb0ef41Sopenharmony_ci # something else we don't care about, we could warn here 1051cb0ef41Sopenharmony_ci else: 1061cb0ef41Sopenharmony_ci yield None 107