跳转到主要内容

支持远程资源支持的模板配方。

项目描述

收集模板生成文件的食谱,该文件可以是内联的或通过构建下载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

还可以使用相同的文件为 renderedonce

>>> 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 (22.4 kB 查看哈希值)

上传时间

由以下支持