配置¶
介绍¶
invoke提供了一个多方面的配置机制,允许您通过配置文件、环境变量的层次结构来配置核心行为和任务的行为, task namespaces 和cli标志。
配置查找、加载、解析和合并的最终结果是 Config 对象,其行为类似于(嵌套的)python字典。invoke在运行时引用此对象(确定以下方法的默认行为 Context.run )并将其作为 Context.config 或作为快捷属性访问 Context 本身。
配置层次结构¶
简言之,配置值相互覆盖的顺序如下:
内部默认值 对于通过配置可以控制的行为。见 默认配置值 有关详细信息。
Collection-driven configurations 通过在任务模块中定义
Collection.configure. (见 Collection -基于配置 详情见下文。)子集合的配置被合并到顶级集合中,最终结果构成整个配置设置的基础。
System-level configuration file 存储在
/etc/,如/etc/invoke.yaml. (见 配置文件 有关此项和其他配置文件项的详细信息。)User-level configuration file 在正在运行的用户的主目录中找到,例如。
~/.invoke.yaml.Project-level configuration file 生活在你的顶层
tasks.py. 例如,如果运行调用加载/home/user/myproject/tasks.py(参见我们的文档 the load process ),这可能是/home/user/myproject/invoke.yaml.环境变量 在调用shell环境中找到。
这些名称空间的层次性不如其他名称空间强,shell环境名称空间也不是完全由invoke拥有,因此我们必须依赖稍微冗长的前缀-请参见 环境变量 有关详细信息。
运行时配置文件 其路径被赋予
-f,例如inv -f /random/path/to/config_file.yaml. 此路径也可以通过INVOKE_RUNTIME_CONFIGEV V.Command-line flags 对于某些核心设置,例如
-e.用户代码所做的修改 在运行时。
默认配置值¶
下面是所有配置值和/或节invoke本身用于控制以下行为的列表 Context.run 的 echo 和 pty 标志、任务重复数据消除等。
备注
这些值的存储位置在 Config 类的返回值。 Config.global_defaults ;有关更多详细信息,请参阅其api文档。
为了方便起见,我们使用点式语法来引用嵌套的设置名称,例如 foo.bar 引用将是什么(在python配置上下文中) {{'foo': {{'bar': <value here>}}}} . 通常,可以读取或设置这些。 Config 和 Context 使用看起来几乎相同的属性语法的对象: c.foo.bar .
这个
tasks配置树保存与任务执行相关的设置。tasks.dedupeControls 任务重复数据消除 默认值为True. 它也可以在运行时被重写。--no-dedupe.tasks.auto_dash_names控制任务和集合名称是否在cli上将下划线转换为破折号。违约:True. 也见 破折号与下划线 .tasks.collection_name控制由 collection discovery ,默认为"tasks".tasks.executor_class允许用户重写实例化并用于任务执行的类。必须是窗体的完全限定的点路径
module(.submodule...).class,除了.class将交给importlib.import_module和class应该是该结果模块对象的属性。默认为
None,意思是用跑步Program对象的executor_class属性。警告
如果将此设置与 custom program binaries ,因为自定义程序可以指定自己的默认执行器类(使用此设置将覆盖该类!)并假设某些行为源于此。
tasks.ignore_unknown_help(默认:False)允许用户禁用“为不存在的参数提供帮助键”错误。正常情况下,Invoke假定这种情况意味着help参数为@task,但有时用户有很好的理由这样做。tasks.search_root允许重写默认值 collection discovery 根搜索位置。它默认为None,它指示使用正在执行的进程的当前工作目录。
这个
run树控制的行为Runner.run. 此树的每个成员(例如run.echo或run.pty)直接映射到Runner.run同名的关键字参数;有关这些设置的作用及其默认值的详细信息,请参见该方法的docstring。这个
runners树控件 _which_ 运行程序类映射到哪个执行上下文;如果您自己使用invoke,则这将只倾向于有一个成员,runners.local. 客户端库可以使用额外的键/值对来扩展它,例如runners.remote.这个
sudo树控制的行为Context.sudo:sudo.password控制提交到sudo密码提示的自动响应密码。违约:None.警告
虽然可以像其他任何设置一样将此设置存储在 configuration files --这样做本质上是不安全的。我们强烈建议在运行时从某种机密管理系统中填充此配置值。
sudo.prompt保存sudo密码提示文本,这两个文本都提供给sudo -p,并在执行时搜索 auto-response . 违约:[sudo] password:.
顶级配置设置,
debug,控制是否记录调试级别输出;它默认为False.debug可以通过-dcli标志,用于在运行cli分析之后启用调试。它也可以通过INVOKE_DEBUG与常规env vars不同的是,环境变量从执行开始就受到重视,因此对于解析和/或配置加载的故障排除非常有用。一个小配置树,
timeouts,保存各种超时控件。目前,对于invoke,这只包含command子键,用于控制子进程执行超时。客户机代码通常会向该树添加更多内容,而调用本身也可能在将来添加更多内容。
配置文件¶
加载¶
对于上一节中提到的每个配置文件位置,我们搜索以 .yaml , .yml , .json 或 .py ( 按顺序! ),加载我们找到的第一个,忽略可能存在的任何其他。
For example, if Invoke is run on a system containing both /etc/invoke.yml
and /etc/invoke.json, only the YAML file will be loaded. This helps
keep things simple, both conceptually and in the implementation.
格式¶
invoke的配置允许任意嵌套,因此我们的配置文件格式也是如此。下面的三个例子都得到了一个等价于 {{'debug': True, 'run': {{'echo': True}}}} :
YAML
debug: true run: echo: true
JSON
{ "debug": true, "run": { "echo": true } }
Python ::
debug = True run = { "echo": True }
有关详细信息,请参阅这些语言自己的文档。
环境变量¶
环境变量与其他配置设置方法稍有不同,因为它们不提供嵌套配置键的干净方法,而且还隐式地在整个系统的已安装应用程序库中共享。
此外,出于实现方面的考虑,env vars必须由配置层次结构中它们下面的级别预先确定(换句话说-env vars只能用于重写现有的配置值)。如果您需要调用来理解 FOOBAR 环境变量,必须首先声明 foobar 在配置文件或任务集合中设置。
基本规则¶
为了缓解shell名称空间问题,我们只需在所有env vars前面加上 INVOKE_ .
嵌套是通过下划线分隔来执行的,因此一个看起来像 {{'run': {{'echo': True}}}} 在python级别变成 INVOKE_RUN_ECHO=1 在一个典型的贝壳里。见 嵌套与带下划线的名称 下面是关于这个的更多信息。
型铸造¶
由于EnvVAR只能用于重写现有设置,所以给定的设置的前一个值被用来作为我们从shell中返回字符串的向导。
如果当前值是Unicode字符串,则将其替换为来自环境的值,不进行任何强制转换;
如果当前值是
None,它也被替换为环境中的字符串;布尔值设置如下:
0以及空值/字符串(例如SETTING=或unset SETTING,或等)评估为False,任何其他值的计算结果为True.列表和元组当前不受支持,将引发异常;
在将来,我们可以实现便利的转换,例如在逗号上拆分以形成列表;然而,由于用户总是能够自己执行这样的操作,所以它可能不是一个高优先级。
所有其他类型(整数、长整型、浮点数等)都只是用作传入值的构造函数。
例如,a
foobar设置其默认值为整数1将通过int因此FOOBAR=5将产生python值5不是"5".
嵌套与带下划线的名称¶
由于环境变量键是单个字符串,我们必须使用某种形式的字符串解析来允许访问嵌套的配置设置。如前所述,在基本用例中,这仅仅意味着使用下划线字符: {{'run': {{'echo': True}}}} 变成 INVOKE_RUN_ECHO=1 .
但是,当设置名称本身包含下划线时,会引入歧义:is INVOKE_FOO_BAR=baz 相当于 {{'foo': {{'bar': 'baz'}}}} ,或 {{'foo_bar': 'baz'}} ?谢天谢地,因为EnvVAR只能用于修改Python级别或配置文件中声明的设置,所以我们查看CONFIG的当前状态来确定答案。
还有一个角落的案子 both 可能的解释以有效的配置路径存在(例如 {{'foo': {{'bar': 'default'}}, 'foo_bar': 'otherdefault'}} )在这种情况下,我们尊重 Zen of Python 拒绝猜测;相反会出现一个错误,建议用户修改其配置布局或避免使用env vars进行设置。
Collection -基于配置¶
Collection 对象可以包含配置映射,通过 Collection.configure ,和(根据 the hierarchy )这通常形成系统中的最低配置级别。
当集合是 nested ,默认情况下,配置是“向下”合并的:当发生冲突时,更接近根的外部命名空间将获胜,而更接近被调用任务的内部命名空间将获胜。
备注
这里的“内部”任务是指从根目录到包含被调用任务的目录路径上的任务。忽略同级子集合。
这意味着什么的一个简单例子:
from invoke import Collection, task
# This task & collection could just as easily come from
# another module somewhere.
@task
def mytask(c):
print(c['conflicted'])
inner = Collection('inner', mytask)
inner.configure({'conflicted': 'default value'})
# Our project's root namespace.
ns = Collection(inner)
ns.configure({'conflicted': 'override value'})
呼叫的结果 inner.mytask ::
$ inv inner.mytask
override value
实际配置使用示例¶
前几节中有一些小示例;本节提供了一组更逼真的示例,展示了配置系统的工作原理。
安装程序¶
我们将从硬编码其值的半现实任务开始,并逐步使用各种配置机制。建筑用的小模块 Sphinx 文档可能是这样开始的:
from invoke import task
@task
def clean(c):
c.run("rm -rf docs/_build")
@task
def build(c):
c.run("sphinx-build docs docs/_build")
那么也许你重构了构建目标:
target = "docs/_build"
@task
def clean(c):
c.run("rm -rf {}".format(target))
@task
def build(c):
c.run("sphinx-build docs {}".format(target))
我们还可以允许运行时参数化:
default_target = "docs/_build"
@task
def clean(c, target=default_target):
c.run("rm -rf {}".format(target))
@task
def build(c, target=default_target):
c.run("sphinx-build docs {}".format(target))
这个任务模块只适用于一组用户,但是如果我们想允许重用呢?可能有人想将此模块与其他默认目标一起使用。使用配置数据(通过上下文arg提供)配置这些设置通常是更好的解决方案 [1].
通过任务集合配置¶
配置 setting 和 getting api允许将其他“硬编码”默认值移动到配置结构中,下游用户可以自由重新定义该结构。让我们把这个应用到我们的例子中。首先,我们添加一个显式命名空间对象:
from invoke import Collection, task
default_target = "docs/_build"
@task
def clean(c, target=default_target):
c.run("rm -rf {}".format(target))
@task
def build(c, target=default_target):
c.run("sphinx-build docs {}".format(target))
ns = Collection(clean, build)
然后,我们可以将默认的构建目标值移到集合的默认配置中,并通过上下文引用它。此时,我们还将Kwarg默认值更改为 None 因此,我们可以确定是否给出了运行时值。结果:
@task
def clean(c, target=None):
if target is None:
target = c.sphinx.target
c.run("rm -rf {}".format(target))
@task
def build(c, target=None):
if target is None:
target = c.sphinx.target
c.run("sphinx-build docs {}".format(target))
ns = Collection(clean, build)
ns.configure({'sphinx': {'target': "docs/_build"}})
结果并没有比我们开始时复杂得多,接下来我们将看到,用户以各种方式覆盖您的默认值已经很简单了。
配置重写¶
当然,最低级别的重写只是修改本地 Collection 已导入分布式模块的树。例如,如果上述模块作为 myproject.docs 某人可以定义一个 tasks.py 就这样:
from invoke import Collection, task
from myproject import docs
@task
def mylocaltask(c):
# Some local stuff goes here
pass
# Add 'docs' to our local root namespace, plus our own task
ns = Collection(mylocaltask, docs)
然后他们可以把这个添加到底部:
# Our docs live in 'built_docs', not 'docs/_build'
ns.configure({'sphinx': {'target': "built_docs"}})
现在我们有一个 docs 其生成目标默认为的子命名空间 built_docs 而不是 docs/_build . 运行时用户仍然可以通过标志覆盖这个(例如)。 inv docs.build --target='some/other/dir' )和以前一样。
如果您更喜欢配置文件,而不是在python中调整命名空间树,那么这也同样有效;与其将上面的行添加到前面的代码片段中,不如将其放到 tasks.py 已命名 invoke.yaml ::
sphinx:
target: built_docs
对于本例,这种本地到项目的conf文件最有意义,但不要忘记 config hierarchy 提供其他配置方法,这些方法可能适合您的需要。
脚注