支持远程资源支持的模板配方。
项目描述
收集模板生成文件的食谱,该文件可以是内联的或通过构建下载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 |