快速入门

这份文档介绍了 Invoke 的功能集的旋风之旅。请参阅贯穿全文的链接,了解详细的概念和 API 文档。关于安装帮助,请看项目的 安装页面

定义和运行任务函数

Invoke 的核心用例是设置任务函数的集合并执行它们。这很容易 – 你只需要做一个叫做 tasks.py 的文件,导入 task 装饰器并装饰一个或多个函数。你还需要添加一个任意命名的上下文参数(惯例是使用 cctxcontext)作为第一个位置参数。先不要担心使用这个上下文参数。

让我们从 dummy Sphinx 文档构建任务开始吧

from invoke import task

@task
def build(c):
    print("Building!")

然后你可以通过告诉 Invoke 的命令行运行器 invoke,你想让它运行来执行这个新任务

$ invoke build
Building!

函数主体可以是你想要的任何 Python – 任何东西。

任务参数

函数可以有参数,因此任务也可以有参数。默认情况下,你的任务函数的 args/kwargs 会自动映射到长和短的 CLI 旗标上,根据 CLI docs。例如,如果我们添加一个 clean 参数,并给它一个布尔默认值,它将显示为一组切换旗标,--clean-c

@task
def build(c, clean=False):
    if clean:
        print("Cleaning!")
    print("Building!")

Invocations:

$ invoke build -c
$ invoke build --clean

自然地,其他默认参数值将允许给出字符串或整数值。没有默认值的参数被假定为采取字符串,并且也可以作为位置参数给出。以这个令人难以置信的编造的片段为例

@task
def hi(c, name):
    print("Hi {}!".format(name))

它可以通过以下方式被调用,都会产生 “Hi Name!”

$ invoke hi Name
$ invoke hi --name Name
$ invoke hi --name=Name
$ invoke hi -n Name
$ invoke hi -nName

通过 @task 添加元数据

@task 可以在没有任何参数的情况下使用,如上所述,但它也是一个方便的矢量,用于描述它所装饰的任务函数的额外元数据。一个常见的例子是通过 help 参数来描述任务的参数(此外还可以通过 docstring 提供任务级别的帮助):

@task(help={'name': "Name of the person to say hi to."})
def hi(c, name):
    """
    Say hi to someone.
    """
    print("Hi {}!".format(name))

当调用 --help 时,该描述将显示出来

$ invoke --help hi
Usage: inv[oke] [--core-opts] hi [--options] [other tasks here ...]

Docstring:
  Say hi to someone.

Options:
  -n STRING, --name=STRING   Name of the person to say hi to.

关于任务参数化和元数据的更多细节可以在 Invoking tasks (关于命令行和解析方面)和 task API 文档(关于声明方面)找到。

列出任务

你有时想看看在给定的 tasks.py 中有哪些任务 – invoke 可以被告知列出它们,而不是执行什么

$ invoke --list
Available tasks:

    build

这也将打印每个任务的文件串的第一行,如果它有的话。要想知道除了 --list 还有什么可用的,请说 invoke --help

运行 shell 命令

Invoke 的许多用例涉及运行本地 shell 命令,类似于 Make 或 Rake 等程序。这是通过 run 函数完成的

from invoke import task

@task
def build(c):
    c.run("sphinx-build docs docs/_build")

在运行过程中,你会在你的终端看到该命令的输出

$ invoke build
Running Sphinx v1.1.3
loading pickled environment... done
...
build succeeded, 2 warnings.

run 有许多参数控制它的行为,例如为需要伪终端的复杂程序激活伪终端,抑制错误时退出的行为,隐藏子进程的输出(同时仍然捕获它以便以后审查),等等。详见 API 文档

run 总是返回一个有用的 Result 对象,提供访问捕获的输出、退出代码和其他信息。

旁白:这个 ‘context’ 的说法到底是什么?

任务运行者面临的一个常见问题是传输 “global” 数据 – 从 配置文件其他配置张量 加载的值,通过 CLI 旗标给出,在 ‘setup’ 任务中生成,等等。

一些库(如 Fabric 1.x)通过模块级的属性来实现,这使得测试困难和容易出错,限制了并发性,并增加了实施的复杂性。

Invoke 将状态封装在明确的 Context 对象中,在任务执行时交给它们。上下文是主要的 API 端点,提供尊重当前状态的方法(如 Context.run),以及对该状态本身的访问。

声明预设任务

任务可以通过 task 装饰器以多种方式进行配置。其中之一是选择一个或多个你希望在执行你的任务之前始终运行的其他任务,用名称表示。

让我们用新的清理任务来扩展文档构建器,该任务在每次构建前运行(当然,它仍然可以单独执行)

from invoke import task

@task
def clean(c):
    c.run("rm -rf docs/_build")

@task(clean)
def build(c):
    c.run("sphinx-build docs docs/_build")

现在当你 invoke build 时,它会自动先运行 clean

备注

如果你不喜欢隐式的 “positional arguments are pre-run task names” 的 API,你可以显式地给出 pre 的 kwarg:@task(pre=[clean])

详情可在 How tasks run 中找到。

创建命名空间

现在,我们的 tasks.py 隐含地只用于文档,但也许我们的项目需要其他非文档的东西,比如打包/部署、测试等等。在这一点上,一个单一的平面命名空间是不够的,所以 Invoke 让你轻松建立一个 嵌套命名空间。这里有一个快速的例子。

首先让我们把 tasks.py 重命名为 docs.py;那里不需要做其他改动。然后我们创建一个新的 tasks.py,为了简洁起见,用一个新的、真正的顶级任务 deploy 来填充它”

最后,我们可以使用新的 API 成员,Collection 类,将这个任务和 docs 模块绑定到一个明确的命名空间。当 Invoke 加载你的任务模块时,如果 Collection 对象作为 nsnamespace 绑定存在,它将被用于根命名空间

from invoke import Collection, task
import docs

@task
def deploy(c):
    c.run("python setup.py sdist")
    c.run("twine upload dist/*")

namespace = Collection(docs, deploy)

结果

$ invoke --list
Available tasks:

    deploy
    docs.build
    docs.clean

关于命名间距如何工作的更详细的分类,请参见 文档