我试图从Jinja2模板中获取所有未定义的变量。 假设我有一个如下所示的模板。
tmpstr = """
{% for row in csv %}
sample {{row.field1}} stuff {{row.field2}} morestuff {{row.field3}}
{% endfor %}
"""
并输入字典如下
cxt = {'csv': [
{'field3': 1234, 'field4': 12314},
{'field3': 2222, 'field4': 1213}
]}
以下是我尝试渲染的方法。
env = Environment(undefined=Undefined)
tmp = env.from_string(tmpstr)
tmpsrc = tmp.render(cxt)
print(tmpsrc)
模板期望变量field1
,field2
和field3
存在。但是field1
和field2
不存在。我的目标是找到所有缺失的变量。
Jinja2默默地忽略了缺失的变量。因此,我尝试添加StrictUndefined
选项:
errs = []
try:
env = Environment(undefined=StrictUndefined)
tmp = env.from_string(tmpstr)
tmpsrc = tmp.render(cxt)
except Exception as e:
errs.append(str(e))
print(errs)
但是这次jinja2抱怨只有第一个缺失变量field1
。
因此我尝试了另一个选项DebugUndefined
。
此选项不会引发异常,并且在模板输出中保留缺少的变量占位符。因此我无法收集缺失的变量。
您能否建议我如何在jinja2模板中找到丢失的变量?
如果有人想尝试一下,这是可运行的代码:
from jinja2 import BaseLoader,Environment,StrictUndefined,DebugUndefined,Undefined
tmpstr = """
{% for row in csv %}
sample {{row.field1}} stuff {{row.field2}} morestuff {{row.field3}}
{% endfor %}
"""
cxt = {'csv': [
{'field3': 1234, 'field4': 12314},
{'field3': 2222, 'field4': 1213}
]}
env = Environment(undefined=Undefined)
tmp = env.from_string(tmpstr)
tmpsrc = tmp.render(cxt)
print('CASE 1: undefined=Undefined')
print(tmpsrc)
errs = []
try:
env = Environment(undefined=StrictUndefined)
tmp = env.from_string(tmpstr)
tmpsrc = tmp.render(cxt)
except Exception as e:
errs.append(str(e))
print('CASE 2: undefined=StrictUndefined')
print(errs)
errs = []
try:
env = Environment(undefined=DebugUndefined)
tmp = env.from_string(tmpstr)
tmpsrc = tmp.render(cxt)
except Exception as e:
errs.append(str(e))
print('CASE 3: undefined=DebugUndefined')
print(errs)
print(tmpsrc)
答案 0 :(得分:5)
我使用jinja2.make_logging_undefined
找到了问题的解决方案。我和你在同一条船上,一直寻找高低的答案。大部分答案都指向我使用已解析的模板,但是我无法弄清楚如何将上下文放入已解析的模板中。
我终于可以使用make_logging_undefined
来完成这项工作了。如果要查找所有未定义的变量,请确保仅使用Undefined
基类而不是StrictUndefined
。使用StrictUndefined
将导致jinja在第一次遇到undefine时抛出异常。
只是一个免责声明:我不是python也不是jinja专家,因此代码不是最有效的也不是结构化的。但这符合我的目的。这只是POC代码。
以下是代码:
import jinja2
import logging
from jinja2 import Environment, Undefined
from jinja2.exceptions import UndefinedError
def main():
templateLoader = jinja2.FileSystemLoader( searchpath="D:\\somelocation\\" )
logging.basicConfig()
logger = logging.getLogger('logger')
LoggingUndefined = jinja2.make_logging_undefined(logger=logger,base=jinja2.Undefined)
templateEnv = jinja2.Environment( loader=templateLoader, undefined=LoggingUndefined)
TEMPLATE_FILE = "./example1.jinja"
template = templateEnv.get_template( TEMPLATE_FILE )
FAVORITES = [ "chocolates", "lunar eclipses", "rabbits" ]
# Specify any input variables to the template as a dictionary.
templateVars = { "title" : "Test Example",
"description" : "A simple inquiry of function.",
"favorites" : FAVORITES,
"whatever" : "1"
}
# Finally, process the template to produce our final text.
try:
outputText = template.render( templateVars )
except ( UndefinedError) as err:
print err
if __name__ == '__main__':
main()
example1.jinja:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{{ title }}</title>
<meta name="description" content="{{ description }}" />
</head>
<body>
<div id="content">
<p>Greetings visitor! These are a list of my favorite things:</p>
<ul>
{% for item in favorites %}
<li>{{ item }}</li>
<li>My favorites: {{ favorites[1] }} </li>
{% endfor %}
{{ undefined_var1 }}
{{ underfined_var2 }}
</ul>
</div>
</body>
</html>
以下是示例输出:
WARNING:logger:Template variable warning: undefined_var1 is undefined
WARNING:logger:Template variable warning: underfined_var2 is undefined
答案 1 :(得分:1)
将Codable
与find_undeclared_variables
配合使用,可以正确地引发一个异常,其中提到所有丢失的变量:
DebugUndefined
如果您希望使用日志记录,则可以使用import jinja2
from jinja2.meta import find_undeclared_variables
env = jinja2.Environment(undefined=jinja2.DebugUndefined)
template = env.from_string('foo={{ foo }}, bar={{ bar}}, baz={{ baz }}')
# Render template without passing all variables
rendered = template.render(foo=1)
# Check if rendering was done correctly
ast = env.parse(rendered)
undefined = find_undeclared_variables(ast) # {'bar', 'baz'}
if undefined:
raise jinja2.UndefinedError(f'The following variables are undefined: {undefined!r}')
的内容将异常引发引发替换为您自己的日志记录调用。
PS:我是Jinja的新手,但我很惊讶这不是undefined
的默认行为。我不知道为什么作者/维护者会认为默认情况下默默忽略缺少变量是一件好事...
答案 2 :(得分:1)
关于您的第一次尝试(在此转贴)
errs = []
try:
env = Environment(undefined=StrictUndefined)
tmp = env.from_string(tmpstr)
tmpsrc = tmp.render(cxt)
except Exception as e:
errs.append(str(e))
print(errs)
我相信问题是:1)当您尝试在脚本中循环时,您正在尝试在模板中循环; 2)在每次异常后都没有更新cxt。
我需要使用自定义定界符对模板做同样的事情(find_undeclared_variables
对此无效)
我使用了这样的东西:
def findAllUndefined(target):
jinja_env = jinja2.Environment(undefined=jinja2.StrictUndefined)
doc = DocxTemplate(target)
context = {}
finished = False
while finished == False:
try:
doc.render(context, jinja_env)
finished = True
except Exception as e:
tag = re.sub(" is undefined", "", str(e)) # extracting tag name from error message
tag = re.sub("'", "", tag)
context[str(tag)] = "FOUND"
return context.keys()
这样的想法是,每次遇到未定义的变量时,将标记名称插入带有绒毛值的上下文中,然后再次尝试进行渲染,直到所有变量都已知并归类为止。
答案 3 :(得分:0)
您可以简单地创建自己的“未定义”,例如以编程方式处理未定义变量的列表。这是一个示例:
missing_vars=[]
class CollectingUndefined(jinja2.Undefined):
def _add_missing_var(self):
missing_vars.append(self._undefined_name)
def __iter__(self):
self._add_missing_var()
return super().__iter__();
def __str__(self):
self._add_missing_var()
return super().__str__();
def __len__(self):
self._add_missing_var()
return super().__len__();
def __eq__(self):
self._add_missing_var()
return super().__eq__();
def __ne__(self):
self._add_missing_var()
return super().__eq__();
def __bool__(self):
self._add_missing_var()
return super().__e__bool__q__();
def __hash__(self):
self._add_missing_var()
return super().__hash__();