配置¶
介绍¶
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_CONFIG
EV 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.dedupe
Controls 任务重复数据消除 默认值为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
可以通过-d
cli标志,用于在运行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 提供其他配置方法,这些方法可能适合您的需要。
脚注