调用任务¶
本页将从解析器机制(如何将任务的参数公开为命令行选项)和执行策略(哪些任务实际运行,以及按什么顺序)两方面解释如何在cli上调用任务。
(有关Invoke的核心标志和选项的详细信息,请参见 inv[oke] 核心用途 )
基本命令行布局¶
调用可以执行为 invoke
(或) inv
简而言之)它的命令行布局如下:
$ inv [--core-opts] task1 [--task1-opts] ... taskN [--taskN-opts]
坦白地说,invoke's CLI parser
将命令行拆分为多个“parser contexts
”,允许它对将接受的参数和选项进行推理:
在给出任何任务名称之前,解析器处于“core”解析上下文中,并查找核心选项和标志,如
--echo
,--list
或--help
.任何非参数类型的标记(例如
mytask
)导致切换到每个任务上下文(如果在 loaded collection )此时,像标记这样的参数应该与先前命名任务的参数相对应(请参见 任务命令行参数 )
然后这个循环无限重复,允许链式执行任意数量的任务。(实际上,大多数用户一次只执行一个或两个。)
有关核心参数和标志,请参见 inv[oke] 核心用途 ;有关任务如何影响cli的详细信息,请继续阅读。
备注
对于解析上下文的行为有一个小小的例外:核心选项 may 也可以在每个任务上下文中给出, 当且仅当 与正在分析的任务的类似命名/前缀参数没有冲突。
例如, invoke mytask --echo
会表现得和 invoke --echo mytask
, 除非 mytask
有它自己的 echo
标志(在这种情况下,该标志会像正常情况一样传递给任务上下文)。
同样地, invoke mytask -e
也将启用命令回显,除非 mytask
有自己的参数,其shortflag设置为 -e
(例如) def mytask(env)
)
任务命令行参数¶
最简单的任务调用,对于不需要参数化的任务:
$ inv mytask
任务可以采用标志参数形式的参数:
$ inv build --format=html
$ inv build --format html
$ inv build -f pdf
$ inv build -f=pdf
请注意,长样式标志和短样式标志都受支持,而等号在这两种情况下都是可选的。
布尔选项是没有参数的简单标志:
$ inv build --progress-bar
当然,一次可以给出多个标志:
$ inv build --progress-bar -f pdf
型铸造¶
从本机上讲,命令行字符串就是——一个字符串——需要一些逻辑跳跃才能在python端得到任何非字符串值。invoke已经掌握了许多这样的技巧,将来还会实现更多的技巧。目前:
具有默认值的参数使用这些默认值作为类型提示,因此
def mytask(c, count=1)
将看到inv mytask --count=5
并得到python整数值5
而不是字符串"5"
.默认值
None
实际上与完全没有默认值是一样的-不进行类型转换,只留下一个字符串。
前一个规则的主要异常是booleans:
True
或False
使这些参数显示为实际的非值接受标志 (--argname
将值设置为True
如果默认为False
或--no-argment
在相反的情况下)。见 自动布尔逆标志 更多示例。列表值(无论如何,您都不想将其设置为参数的默认值——这是python的一个常见错误)由一个特殊的
@task
旗见 Iterable标志值 下面。目前没有办法在命令行上设置其他复合值(如dict);解决这个更复杂的问题留给读者作为练习(尽管我们将来可能会为此类事情添加帮助程序)。
按任务帮助/打印可用标志¶
若要获得特定任务的帮助,可以将任务名称作为参数传递给核心。 --help
/-h
选择,或给予 --help
/-h
在任务之后(这将触发自定义的-help``行为,其中任务名称本身被赋予 ``--help
作为其参数值)。
当请求帮助时,您将看到任务的docstring(如果有的话)和每个参数/标志的帮助输出:
$ inv --help build # or invoke build --help
Docstring:
none
Options for 'build':
-f STRING, --format=STRING Which build format type to use
-p, --progress-bar Display progress bar
全球短旗¶
布尔短标志可以组合成一个标志表达式,例如:
$ inv build -qv
相当于(并在解析期间扩展为):
$ inv build -q -v
如果globbed short flag标记中的第一个标志不是布尔值,而是接受一个值,则将glob的其余部分改为该值。例如。::
$ inv build -fpdf
扩展为:
$ inv build -f pdf
和 not ::
$ inv build -f -p -d -f
可选标志值¶
你看到这个暗示 --help
特别是,如果声明为 optional
. 例如,假设您的任务有 --log
激活日志记录的标志:
$ inv compile --log
但你也希望它是可配置的 在哪里? 登录::
$ inv compile --log=foo.log
你可以用一个附加的参数(例如 --log
和 --log-location
)但有时简洁的api更有用。
若要启用此功能,请在内部指定此“混合”可选值类型的参数 @task
::
@task(optional=['log'])
def compile(c, log=None):
if log:
log_file = '/var/log/my.log'
# Value was given, vs just-True
if isinstance(log, str):
log_file = log
# Replace w/ your actual log setup...
set_log_destination(log_file)
# Do things that might log here...
使用可选标志值时,分析后看到的值遵循以下规则:
如果旗子根本不给的话 (
invoke compile
)默认值按正常值填写。如果给它一个值 (
invoke compile --log=foo.log
)然后正常存储值。如果给定的标志没有值 (
invoke compile --log
,它被视为bool
并设置为True
.
解决歧义¶
对于采用可选值的标志,有许多情况下可能会产生歧义:
当一个任务接受位置参数时,当解析器到达可选值标志时,它们还没有全部被填充;
当这些标志中的一个后面的标记看起来本身就是一个标志时;或者
当该令牌与另一个任务同名时。
在大多数情况下,invoke的解析器将 refuse the temptation to guess 并提出一个错误。
但是,如果二义性标记类似于标志,则会检查当前解析上下文以解决二义性:
如果令牌是合法的参数,则假定用户打算在当前参数之后立即给出该参数,并且不设置可选值。
例如在
invoke compile --log --verbose
(假设)--verbose
是另一个合法的理由compile
)解析器决定用户打算给--log
没有值,然后--verbose
旗帜。
否则,将逐字解释令牌并将其存储为当前标志的值。
例如如果
--verbose
是 not 正当理由compile
然后invoke compile --log --verbose
使分析器指定"--verbose"
作为给定的值--log
. (这可能会在我们设计的用例中引起其他问题,但它说明了我们的观点。)
Iterable标志值¶
cli程序的一个常见用例是希望为给定的选项构建一个值列表,而不是单个值。虽然这 can 通过子字符串解析完成——例如让用户使用 --mylist item1,item2,item3
在逗号上拆分——通常最好多次指定选项并将值存储在列表中(而不是重写或出错)。
在invoke中,通过向解析器暗示一个或多个任务参数是 iterable
本质上(类似于 optional
或 positional
):
@task(iterable=['my_list'])
def mytask(c, my_list):
print(my_list)
如果根本没有给定,则 my_list
将是一个空列表;否则,结果是一个列表,按顺序追加看到的每个值,而不进行任何其他操作(因此无重复数据消除等):
$ inv mytask
[]
$ inv mytask --my-list foo
['foo']
$ inv mytask --my-list foo --my-list bar
['foo', 'bar']
$ inv mytask --my-list foo --my-list bar --my-list foo
['foo', 'bar', 'foo']
递增标志值¶
这可以说是 iterable flag values (如上所示)-它具有相同的核心接口“多次给出一个cli参数,并使其执行除错误或覆盖单个值以外的操作”。但是,“incrementables”(如您可能猜到的)会增加一个整数,而不是生成一个字符串列表。这通常出现在详细标志和类似功能中。
举个例子:
@task(incrementable=['verbose'])
def mytask(c, verbose=0):
print(verbose)
它的用途:
$ inv mytask
0
$ inv mytask --verbose
1
$ inv mytask -v
1
$inv mytask -vvv
3
很高兴,因为在python中 0
是假的 1
(或任何其他数字)是“truthy”,它的功能也很像布尔标志,至少有一个默认值是 0
.
备注
您可以为此类参数提供任何整数默认值(它只是作为起始值),但请注意,参数的使用者在编写时应理解,除非 0
你说什么?
标志名称中的破折号与下划线¶
在python中,使用 underscored_names
对于关键字参数,例如:
@task
def mytask(c, my_option=False):
pass
但是,命令行标志的典型约定是破折号,它在python标识符中无效:
$ inv mytask --my-option
invoke通过自动生成带下划线名称的虚线版本来解决这个问题,当它将任务函数签名转换为命令行解析器标志时。
因此,上面的两个例子实际上很好地结合在一起-- my_option
最终映射到 --my-option
.
此外,领先 (_myopt
)和拖尾 (myopt_
)下划线被忽略,因为 invoke ---myopt
和 invoke --myopt-
没什么意义。
自动布尔逆标志¶
当设置通常是 False
,至 True
::
$ inv mytask --yes-please-do-x
但是,在某些情况下,您希望相反的结果是 True
,它可以很容易地被禁用。例如,彩色输出:
@task
def run_tests(c, color=True):
# ...
在这里,我们真正想要的是命令行 --no-color
设置的标志 color=False
. invoke为您处理这个问题:设置cli标志时,boolean默认为 True
生成一个 --no-<name>
改为打旗子。
任务如何运行¶
基本情况¶
在最简单的情况下,没有前置或后置任务的任务只运行一次。例子::
@task
def hello(c):
print("Hello, world!")
执行::
$ inv hello
Hello, world!
任务前和任务后¶
应该在任务之前或之后执行另一个任务的任务,可以使用 @task
分离器 pre
和/或 post
夸格斯,就像这样:
@task
def clean(c):
print("Cleaning")
@task
def publish(c):
print("Publishing")
@task(pre=[clean], post=[publish])
def build(c):
print("Building")
执行::
$ inv build
Cleaning
Building
Publishing
这些关键字参数总是带iterables。为了方便起见,pre tasks(仅限pre tasks)可以作为位置参数给出,其方式类似于 make
. 我们可以将上述示例的一部分表示为:
@task
def clean(c):
print("Cleaning")
@task(clean)
def build(c):
print("Building")
像以前一样, invoke build
会导致 clean
然后跑 build
.
递归/链式前/后任务¶
前置任务的前置任务也将以深度优先的方式递归调用(前置任务的后置任务、后置任务的前置任务等也将如此)。下面是一个更复杂(如果稍微做作的话)的任务文件:
@task
def clean_html(c):
print("Cleaning HTML")
@task
def clean_tgz(c):
print("Cleaning .tar.gz files")
@task(clean_html, clean_tgz)
def clean(c):
print("Cleaned everything")
@task
def makedirs(c):
print("Making directories")
@task(clean, makedirs)
def build(c):
print("Building")
@task(build)
def deploy(c):
print("Deploying")
有了深度优先的行为,下面希望对大多数用户来说是直观的:
$ inv deploy
Cleaning HTML
Cleaning .tar.gz files
Cleaned everything
Making directories
Building
Deploying
参数化前/后任务¶
默认情况下,执行前任务和后任务没有参数,即使触发其执行的任务被赋予了一些。当这不合适时,您可以用 call
允许您指定调用签名的对象:
@task
def clean(c, which=None):
which = which or 'pyc'
print("Cleaning {}".format(which))
@task(pre=[call(clean, which='all')]) # or call(clean, 'all')
def build(c):
print("Building")
示例输出:
$ inv build
Cleaning all
Building
任务重复数据消除¶
默认情况下,在会话期间运行多次的任何任务(例如,由于包含在前/后任务中)将只运行一次。任务文件示例:
@task
def clean(c):
print("Cleaning")
@task(clean)
def build(c):
print("Building")
@task(build)
def package(c):
print("Packaging")
关闭重复数据消除后(见下文),将执行上述操作 clean
> build
> build
再次> package
. 通过重复数据消除, build
不会发生:
$ inv build package
Cleaning
Building
Packaging
备注
参数化预任务(使用 call
)根据参数列表进行重复数据消除。例如,如果 clean
以两种不同的方式被参数化并作为一个前置任务连接起来。 call(clean, 'html')
和 call(clean, 'all')
-如果它们都在同一会话中运行,则不会被重复数据消除。
However, two separate references to call(clean, 'html')
would become
deduplicated.
禁用重复数据消除¶
如果你喜欢你的任务,无论何时运行,不管怎样,你可以给 --no-dedupe
在运行时设置core cli选项,或设置 tasks.dedupe
config setting 到 False
. 虽然这在现实世界中毫无意义,但让我们想象一下我们想申请 --no-dedupe
对于上面的示例;我们将看到以下输出:
$ inv --no-dedupe build package
Cleaning
Building
Building
Packaging
构建步骤现在运行两次。