Foo was given to the template
foo: {{ foo }}
Bar was not, so it is missing
bar is missing: {{ bar == missing }}
{% py %}
# Normal python code in here
# Excess indentation will be removed.
# All template variables are accessible and can be modified.
import numpy as np
a = np.array([1, 2])
m = np.array([[3, 4], [5, 6]])
bar = m @ a * foo
# It's also possible to template the python code.
{% if change_foo %}
foo = 'new foo value'
{% endif %}
print("Stdio is redirected to the output.")
{% endpy %}
Foo will have the new value if you set change_foo to True
foo: {{ foo }}
Bar will now have a value.
bar: {{ bar }}
{% py %}
# The locals from previous blocks are accessible.
m = m**2
{% endpy %}
m:
{{ m }}
如果将模板参数设置为foo=10, change_foo=True,则输出为:
Foo was given to the template
foo: 10
Bar was not, so it is missing
bar is missing: True
Stdio is redirected to the output.
Foo will have the new value if you set change_foo to True
foo: new foo value
Bar will now have a value.
bar: [110 170]
m:
[[ 9 16]
[25 36]]
带有运行示例的主函数的扩展。
from jinja2 import Environment, PackageLoader, nodes
from jinja2.ext import Extension
from textwrap import dedent
from io import StringIO
import sys
import re
import ctypes
def main():
env = Environment(
loader=PackageLoader('python_spike', 'templates'),
extensions=[PythonExtension]
)
template = env.get_template('emb_py2.txt')
print(template.render(foo=10, change_foo=True))
var_name_regex = re.compile(r"l_(\d+)_(.+)")
class PythonExtension(Extension):
# a set of names that trigger the extension.
tags = {'py'}
def __init__(self, environment: Environment):
super().__init__(environment)
def parse(self, parser):
lineno = next(parser.stream).lineno
body = parser.parse_statements(['name:endpy'], drop_needle=True)
return nodes.CallBlock(self.call_method('_exec_python',
[nodes.ContextReference(), nodes.Const(lineno), nodes.Const(parser.filename)]),
[], [], body).set_lineno(lineno)
def _exec_python(self, ctx, lineno, filename, caller):
# Remove access indentation
code = dedent(caller())
# Compile the code.
compiled_code = compile("\n"*(lineno-1) + code, filename, "exec")
# Create string io to capture stdio and replace it.
sout = StringIO()
stdout = sys.stdout
sys.stdout = sout
try:
# Execute the code with the context parents as global and context vars and locals.
exec(compiled_code, ctx.parent, ctx.vars)
except Exception:
raise
finally:
# Restore stdout whether the code crashed or not.
sys.stdout = stdout
# Get a set of all names in the code.
code_names = set(compiled_code.co_names)
# The the frame in the jinja generated python code.
caller_frame = sys._getframe(2)
# Loop through all the locals.
for local_var_name in caller_frame.f_locals:
# Look for variables matching the template variable regex.
match = re.match(var_name_regex, local_var_name)
if match:
# Get the variable name.
var_name = match.group(2)
# If the variable's name appears in the code and is in the locals.
if (var_name in code_names) and (var_name in ctx.vars):
# Copy the value to the frame's locals.
caller_frame.f_locals[local_var_name] = ctx.vars[var_name]
# Do some ctypes vodo to make sure the frame locals are actually updated.
ctx.exported_vars.add(var_name)
ctypes.pythonapi.PyFrame_LocalsToFast(
ctypes.py_object(caller_frame),
ctypes.c_int(1))
# Return the captured text.
return sout.getvalue()
if __name__ == "__main__":
main()
您可以添加到global variables,可以从Jinja模板访问它。您可以将自己的函数定义放在其中,它可以做您需要的任何事情。
目前的答案在几乎所有情况下都是正确的。但是,在一些非常罕见的情况下,您希望在模板中包含python代码。在我的例子中,我想用它来预处理一些latex文件,我更愿意让python代码生成表值、图等,在latex文件中。
所以我做了一个Jinja2扩展,它添加了一个新的“py”块,允许在模板中编写python代码。请记住,我必须做一些有问题的工作来让这个工作,所以我不能百分之百确定在什么情况下,它失败或行为出乎意料。
这是一个示例模板。
如果将模板参数设置为
foo=10, change_foo=True
,则输出为:带有运行示例的主函数的扩展。
不,没有办法把Python内联到Jinja中。但是,您可以通过扩展模板引擎的Environment或对所有模板可用的global namespace来添加Jinja知道的构造。或者,您可以添加一个过滤器,让我们格式化datetime对象。
烧瓶将Jinja2环境存储在^{} 上。您可以通过直接添加到此字典或使用^{} 装饰器将新上下文插入到环境中。
无论您选择哪种路径,都应该在设置应用程序时完成,然后再处理任何请求。(有关如何设置筛选器的一些goodexamples信息,请参见网站的“代码片段”部分-the docs包含添加到全局变量的一个很好的示例)。
相关问题 更多 >
编程相关推荐