支持远程资源支持的模板配方。
项目描述
收集模板生成文件的食谱,该文件可以是内联的或通过构建下载API获取。
灵感来源于 collective.recipe.template。
默认配方
默认食谱使用构建扩展从模板生成文件(选项 output)。模板可以通过 url(可选地与 md5sum 结合)或 inline 指定。
以下是一个简单的构建配置
>>> base = """
... [buildout]
... parts = template
...
... [section]
... option = value
...
... [template]
... recipe = slapos.recipe.template
... url = template.in
... output = template.out
... """
>>> write('buildout.cfg', base)
一个简单的模板
>>> write('template.in', '${section:option}')
输出文件已被构建扩展本身解析
>>> run_buildout()
Installing template.
>>> cat('template.out')
value
该食谱依赖于构建扩展来拉取它依赖的部分,这意味着渲染(包括请求下载)是在初始化阶段完成的。
选项
md5sum - 检查文件完整性
如果模板通过 url 选项指定,可以提供一个 MD5 校验和来检查模板的内容
>>> base += """
... md5sum = 1993226f57db37c4a19cb785f826a1aa
... """
>>> write(sample_buildout, 'buildout.cfg', base)
>>> run_buildout()
Uninstalling template.
Installing template.
>>> cat('template.out')
value
在这种情况下,更新部分没有任何作用
>>> write('template.out', 'altered')
>>> run_buildout()
Updating template.
>>> cat('template.out')
altered
如果校验和不匹配
>>> run_buildout('template:md5sum=00000000000000000000000000000000')
While:
Installing.
Getting section template.
Initializing section template.
Error: MD5 checksum mismatch for local resource at 'template.in'.
内联
您可能更喜欢内联小模板
>>> write('buildout.cfg', """
... [buildout]
... parts = template
...
... [section]
... option = inlined
...
... [template]
... recipe = slapos.recipe.template
... inline = ${section:option}
... output = template.out
... """)
>>> run_buildout()
Uninstalling template.
Installing template.
>>> cat('template.out')
inlined
请注意,在这种情况下,渲染是由构建扩展本身完成的:它只创建一个包含 inline 值的文件。
mode - 指定文件系统权限
默认情况下,如果输出文件的内容看起来像可执行脚本(即它有一个指向可执行文件的 shebang),则设置可执行权限。这是通过尊重 umask 来完成的。
>>> import os, stat
>>> os.access('template.out', os.X_OK)
False
>>> run_buildout('section:option=#!/bin/sh')
Uninstalling template.
Installing template.
>>> os.access('template.out', os.X_OK)
True
可以使用八进制表示法使用 mode 选项强制设置文件权限(不需要 0 前缀)
>>> run_buildout('template:mode=627')
Uninstalling template.
Installing template.
>>> print("0%o" % stat.S_IMODE(os.stat('template.out').st_mode))
0627
jinja2
与默认食谱类似,但模板语法是 Jinja2 而不是构建扩展。其他显著差异包括
渲染和下载(如果请求)是在安装阶段完成的。
依赖关系是显式的(请参阅 context 选项),而不是从模板中推导出来的。
一些额外功能(以下详细说明选项)。
为了向后兼容,以下旧选项仍然支持
可以通过 rendered 而不是 output 指定生成的文件。
可以使用 template 而不是 url/inline 指定模板。内联模板以 inline: + 可选换行符开头。
以下是一些类型的示例
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... url = foo.in
... output = foo
... context =
... key bar section:key
... key recipe :recipe
... raw knight Ni !
... import json_module json
... section param_dict parameter-collection
...
... [parameter-collection]
... foo = 1
... bar = bar
...
... [section]
... key = value
... ''')
和 Jinja2 模板(保持简单,控制结构是可能的)
>>> write('foo.in',
... '{{bar}}\n'
... 'Knights who say "{{knight}}"\n'
... '${this:is_literal}\n'
... '${foo:{{bar}}}\n'
... 'swallow: {{ json_module.dumps(("african", "european")) }}\n'
... 'parameters from section: {{ param_dict | dictsort }}\n'
... 'Rendered with {{recipe}}\n'
... 'UTF-8 text: привет мир!\n'
... 'Unicode text: {{ "你好世界" }}\n'
... )
我们运行构建扩展
>>> run_buildout() Installing template.
模板已渲染
>>> cat('foo')
value
Knights who say "Ni !"
${this:is_literal}
${foo:value}
swallow: ["african", "european"]
parameters from section: [('bar', 'bar'), ('foo', '1')]
Rendered with slapos.recipe.template:jinja2
UTF-8 text: привет мир!
Unicode text: 你好世界
选项
md5sum, mode
与默认食谱相同。
once - 避免文件重新创建
标记文件的路径,以防止完全渲染。
通常,每次安装/更新部分时,都会重新生成文件。在某些情况下,这可能是不希望的。
once 允许指定一个标记文件,如果存在,则防止模板渲染
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... inline = dummy
... output = foo_once
... once = foo_flag
... ''')
>>> run_buildout()
Uninstalling template.
Installing template.
The template install returned None. A path or iterable os paths should be returned.
模板已渲染
>>> cat('foo_once')
dummy
存在金丝雀
>>> import os
>>> os.path.exists('foo_flag')
True
删除渲染文件并重新渲染
>>> os.unlink('foo_once')
>>> with open('buildout.cfg', 'a') as f:
... f.writelines(['extra = useless'])
>>> run_buildout()
Uninstalling template.
Installing template.
The template install returned None. A path or iterable os paths should be returned.
Unused options for template: 'extra'.
模板未渲染
>>> os.path.exists('foo_once')
False
删除金丝雀允许重新渲染模板
>>> os.unlink('foo_flag')
>>> with open('buildout.cfg', 'a') as f:
... f.writelines(['moreextra = still useless'])
>>> run_buildout()
Uninstalling template.
Installing template.
The template install returned None. A path or iterable os paths should be returned.
Unused options for template: 'extra'.
>>> cat('foo_once')
dummy
还可以使用相同的文件为 rendered 和 once
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... inline = initial content
... output = rendered
... once = ${:output}
... ''')
>>> run_buildout() # doctest: +ELLIPSIS
Uninstalling template.
Installing template.
The template install returned None. A path or iterable os paths should be returned.
模板已渲染
>>> cat('rendered')
initial content
当构建扩展选项被修改时,模板不会再次渲染
>>> with open('buildout.cfg', 'a') as f:
... f.writelines(['inline = something different'])
>>> run_buildout()
Uninstalling template.
Installing template.
The template install returned None. A path or iterable os paths should be returned.
即使我们使用了不同的模板,文件仍然包含第一个模板
>>> cat('rendered')
initial content
context - 模板变量和部分依赖关系
Jinja2 上下文指定,每行一个变量,用 3 个空格分隔的部分:类型、名称和表达式。下面描述了可用类型。“名称”是要声明的变量名称。表达式的语义根据类型而变化。
可用类型
- raw
立即字面字符串。
- key
间接字面字符串。
- import
导入 Python 模块。
- section
使整个构建扩展部分作为字典对模板可用。
间接目标指定为:[章节]:键 . 可以使用 buildout 的内置变量替换,而不是 键 类型,但请注意,对于此配方,不同行代表不同的变量。这可能正是您想要的(分解上下文块声明),否则应使用间接类型。
您可以在模板中使用 buildout 的其他部分。这样,这些部分将被安装为依赖项。
>>> write('buildout.cfg', '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... inline = {{bar}}
... output = foo
... context = key bar dependency:foobar
...
... [dependency]
... foobar = dependency content
... recipe = zc.buildout:debug
... ''')
>>> run_buildout()
Uninstalling template.
Installing dependency.
foobar='dependency content'
recipe='zc.buildout:debug'
Installing template.
这样,您可以获取在依赖项配方 __init__ 中计算的选项。
让我们创建一个示例配方,修改其选项字典。
>>> write('setup.py',
... '''
... from setuptools import setup
...
... setup(name='samplerecipe',
... entry_points = {
... 'zc.buildout': [
... 'default = main:Recipe',
... ],
... }
... )
... ''')
>>> write('main.py',
... '''
... class Recipe(object):
...
... def __init__(self, buildout, name, options):
... options['data'] = 'foobar'
...
... def install(self):
... return []
... ''')
让我们只使用 buildout.cfg 并使用此 egg。
>>> write('buildout.cfg',
... '''
... [buildout]
... develop = .
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... inline =
... {{bar}}
... output = foo
... context = key bar sample:data
...
... [sample]
... recipe = samplerecipe
... ''')
>>> run_buildout()
Develop: '/sample-buildout/.'
Uninstalling template.
Uninstalling dependency.
Installing sample.
Installing template.
>>> cat('foo')
foobar
extensions - Jinja2 扩展
在渲染模板时启用 Jinja2 扩展,以空格分隔。默认情况下,不加载任何扩展。
>>> write('foo.in',
... '''{% set foo = ['foo'] -%}
... {% do foo.append(bar) -%}
... {{ foo | join(', ') }}''')
>>> write('buildout.cfg',
... '''
... [buildout]
... develop = .
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... url = foo.in
... output = foo
... context = key bar buildout:parts
... # We don't actually use all those extensions in this minimal example.
... extensions = jinja2.ext.do jinja2.ext.loopcontrols
... jinja2.ext.with_
... ''')
>>> run_buildout()
Develop: '/sample-buildout/.'
Uninstalling template.
Uninstalling sample.
Installing template.
>>> cat('foo')
foo, template
import-delimiter, import-list - 模板导入
import-delimiter 指定了模板导入的分隔符字符。默认为 /。
import-list 是一个导入路径列表。格式类似于 上下文。“名称”成为导入的基本名称。可用类型
- rawfile
文件的文本路径。
- file
文件的间接路径。
- rawfolder
文件夹的文本路径。可以导入此类文件夹中的任何文件。
- folder
文件夹的间接路径。可以导入此类文件夹中的任何文件。
这是一个简单的模板,导入了一个同样简单的库
>>> write('template.in', '''
... {%- import "library" as library -%}
... {{ library.foo() }}
... ''')
>>> write('library.in', '{% macro foo() %}FOO !{% endmacro %}')
要从渲染的模板中导入模板,需要指定可以导入的内容
>>> write('buildout.cfg', '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... url = template.in
... output = bar
... import-list = rawfile library library.in
... ''')
>>> run_buildout()
Uninstalling template.
Installing template.
>>> cat('bar')
FOO !
就像上下文定义一样,它也适用于间接值
>>> write('buildout.cfg', '''
... [buildout]
... parts = template
...
... [template-library]
... path = library.in
...
... [template]
... recipe = slapos.recipe.template:jinja2
... url = template.in
... output = bar
... import-list = file library template-library:path
... ''')
>>> run_buildout()
Uninstalling template.
Installing template.
>>> cat('bar')
FOO !
这也允许从不同目录中的同名列文件中导入
>>> write('template.in', '''
... {%- import "dir_a/1.in" as a1 -%}
... {%- import "dir_a/2.in" as a2 -%}
... {%- import "dir_b/1.in" as b1 -%}
... {%- import "dir_b/c/1.in" as bc1 -%}
... {{ a1.foo() }}
... {{ a2.foo() }}
... {{ b1.foo() }}
... {{ bc1.foo() }}
... ''')
>>> mkdir('a')
>>> mkdir('b')
>>> mkdir(join('b', 'c'))
>>> write(join('a', '1.in'), '{% macro foo() %}a1foo{% endmacro %}')
>>> write(join('a', '2.in'), '{% macro foo() %}a2foo{% endmacro %}')
>>> write(join('b', '1.in'), '{% macro foo() %}b1foo{% endmacro %}')
>>> write(join('b', 'c', '1.in'), '{% macro foo() %}bc1foo{% endmacro %}')
所有模板都可以在两个文件夹中访问
>>> write('buildout.cfg', '''
... [buildout]
... parts = template
...
... [template-library]
... path = library.in
...
... [template]
... recipe = slapos.recipe.template:jinja2
... url = template.in
... output = bar
... import-list =
... rawfolder dir_a a
... rawfolder dir_b b
... ''')
>>> run_buildout()
Uninstalling template.
Installing template.
>>> cat('bar')
a1foo
a2foo
b1foo
bc1foo
可以覆盖默认路径分隔符(对最终路径没有影响)
>>> write('template.in', r'''
... {%- import "dir_a\\1.in" as a1 -%}
... {%- import "dir_a\\2.in" as a2 -%}
... {%- import "dir_b\\1.in" as b1 -%}
... {%- import "dir_b\\c\\1.in" as bc1 -%}
... {{ a1.foo() }}
... {{ a2.foo() }}
... {{ b1.foo() }}
... {{ bc1.foo() }}
... ''')
>>> write('buildout.cfg', r'''
... [buildout]
... parts = template
...
... [template-library]
... path = library.in
...
... [template]
... recipe = slapos.recipe.template:jinja2
... url = template.in
... output = bar
... import-delimiter = \
... import-list =
... rawfolder dir_a a
... rawfolder dir_b b
... ''')
>>> run_buildout()
Uninstalling template.
Installing template.
>>> cat('bar')
a1foo
a2foo
b1foo
bc1foo
update - 强制在更新时重新渲染
默认情况下,如果模板在更新之前已知相同,则不会执行任何操作,无论是内联还是提供了 md5sum
>>> write('buildout.cfg',
... '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... inline = {{ os.environ['FOO'] }}
... output = foo
... context = import os os
... ''')
>>> os.environ['FOO'] = '1'
>>> run_buildout()
Uninstalling template.
Installing template.
>>> cat('foo')
1
>>> os.environ['FOO'] = '2'
>>> run_buildout()
Updating template.
>>> cat('foo')
1
但 Jinja2 如此,输出可能取决于构建出数据之外的其他事物,因此可能需要在这种情况下强制更新
>>> with open('buildout.cfg', 'a') as f:
... f.writelines(['update = true'])
>>> run_buildout()
Uninstalling template.
Installing template.
>>> cat('foo')
2
>>> os.environ['FOO'] = '1'
>>> run_buildout()
Updating template.
>>> cat('foo')
1
>>> del os.environ['FOO']
编码
输入模板和输出文件的编码。默认为 utf-8。
常见问题解答
问题:如何生成 ${foo:bar} 其中 foo 来自变量?
- 答案:{{ '${' ~ foo_var ~ ':bar}' }}
这是必需的,因为 jinja2 无法解析 “${{{ foo_var }}:bar}”。尽管如此,jinja2 成功解析 “${foo:{{ bar_var }}}”,因此在这种情况下不需要此技巧。
模板错误
>>> write('template.in', '''\
... foo
... {%
... bar
... ''')
>>> write('buildout.cfg', '''
... [buildout]
... parts = template
...
... [template]
... recipe = slapos.recipe.template:jinja2
... url = template.in
... output = foo
... ''')
>>> 0; run_buildout() # doctest: +ELLIPSIS
0...
While:
Installing template.
...
Traceback (most recent call last):
...
File "template.in", line 3, in template
bar
...TemplateSyntaxError: Encountered unknown tag 'bar'.
历史记录
5.1 (2022-10-24)
允许覆盖部分取消设置 'url' 以定义 'inline'。
修复 buildout 下载 API 的错误初始化。
5.0 (2022-02-03)
jinja2:在编译源时使用源路径而不是下载目标路径进行注释。
默认:通过重构 2 个配方,引入一些 jinja2 的改进。
默认:添加对内联模板的支持。
改进确定输出文件是否可执行的条件。
jinja2:修复默认上下文(缺少范围,Py2/Py3 不一致)。
jinja2:添加指定源(模板 -> url/inline)和目标(渲染 -> 输出)的新选项,类似于默认配方。已弃用 rendered 和 template。
jinja2:默认情况下,仅在模板可能已更改时才重新渲染更新,并添加了新的布尔选项以强制更新。
4.6 (2021-06-08)
修复从 URL 获取的模板中的临时文件泄漏问题。
4.5 (2020-01-08)
jinja2:防止 'once' 覆写 'rendered'。
4.4 (2019-01-24)
jinja2:添加 bytes 和 six。
4.3 (2018-01-25)
jinja2:仅编译相同的源一次,并在下次使用编译后的源。
4.2 (2017-12-12)
jinja2:尝试在没有更改时不在更新时重写。
4.1 (2017-10-18)
修复基本模板中的 $$ 转义。
4.0 (2017-10-13)
jinja2:在安装/更新时读取模板并修复 'mode' 选项。
添加对 Python 3 的支持。
3.0 (2017-05-23)
jinja2:使 'import' 返回叶子模块而不是根模块。
2.10 (2017-01-18)
jinja2:添加对 render-once 的支持。
2.9 (2015-11-18)
jinja2:添加对非ASCII模板的支持。输入/输出和导入文件的编码可以通过新的“encoding”参数设置,默认为utf-8。
2.8 (2015-06-25)
jinja2:新增assert函数。
2.7 (2015-05-18)
jinja2:修复在根模板(或实例参数)中出错时跟踪回溯中源代码的显示。
2.6 (2014-11-26)
jinja2:添加许多Python内置函数。
2.5 (2013-08-07)
修复Jinja2 >= 2.7的文件导入问题。
2.4.3 (2013-08-02)
jinja2:添加对内联模板的支持。
2.4.2 (2012-08-21)
jinja2:应使用模式而不是umask。[Vincent Pelletier]
jinja2:添加对jinja2“import”指令的支持。[Vincent Pelletier, Timothee Lacroix]
添加了rawfile和rawfolder类型。[Vincent Pelletier, Timothee Lacroix]
重构了加载器类。[Vincent Pelletier]
2.4.1 (2012-08-01)
jinja2:使“context”参数真正成为可选的。[Vincent Pelletier]
2.4 (2012-06-01)
当存在时提供对zc.buildout.buildout.dumps的访问。[Vincent Pelletier]
修复了包描述中缺失的jinja2入口点文档。[Vincent Pelletier]
2.3 (2012-03-29)
添加了带有jinja2模板支持的jinja2入口点。[Vincent Pelletier]
2.2 (2011-10-12)
包含包中缺失的文件。[Łukasz Nowak]
2.1 (2011-10-12)
更新描述。[Łukasz Nowak]
2.0 (2011-10-12)
移除collective.recipe.template依赖。[Romain Courteaud, Antoine Catton]
1.1 (2011-05-30)
为了最小化依赖,从slapos.cookbook中移除。[Łukasz Nowak]
项目详情
slapos.recipe.template-5.1.tar.gz的哈希值
| 算法 | 哈希摘要 | |
|---|---|---|
| SHA256 | a6a2f17a3e0175f410d5ae79b146c9b778cf8da8f9e024486cc233df8a08a49b |
|
| MD5 | 361adae39a293ffeb82a05b67489eb6e |
|
| BLAKE2b-256 | 8c9dacac5106c373692e227cf000e862ff7faa16df23ee237184b4e9007ae1f2 |