命令和组 — 单击文档

来自菜鸟教程
Click/docs/7.x/commands
跳转至:导航、​搜索

命令和组

Click 最重要的特性是任意嵌套命令行实用程序的概念。 这是通过 CommandGroup(实际上是 MultiCommand)实现的。

回调调用

对于常规命令,只要命令运行就会执行回调。 如果脚本是唯一的命令,它将始终触发(除非参数回调阻止它。 例如,如果有人将 --help 传递给脚本,就会发生这种情况)。

对于组和多命令,情况看起来不同。 在这种情况下,只要子命令触发,回调就会触发(除非此行为被更改)。 这在实践中意味着当内部命令运行时外部命令运行:

这是它的样子:


传递参数

Click 将命令和子命令之间的参数严格分开。 这意味着特定命令的选项和参数必须在 命令名称本身之后指定 ,但在 任何其他命令名称之前指定

使用预定义的 --help 选项已经可以观察到这种行为。 假设我们有一个名为 tool.py 的程序,其中包含一个名为 sub 的子命令。

  • tool.py --help 将返回整个程序的帮助(列出子命令)。
  • tool.py sub --help 将返回 sub 子命令的帮助。
  • 但是 tool.py --help sub 会将 --help 视为主程序的参数。 Click 然后调用 --help 的回调,在 click 可以处理子命令之前打印帮助并中止程序。


嵌套处理和上下文

从前面的示例中可以看出,基本命令组接受传递给其回调的调试参数,但不接受同步命令本身。 sync 命令只接受它自己的参数。

这允许工具彼此完全独立地运行,但是一个命令如何与嵌套的命令通信呢? 答案是 Context

每次调用命令时,都会创建一个新的上下文并与父上下文链接。 通常,您看不到这些上下文,但它们确实存在。 上下文与值一起自动传递给参数回调。 命令还可以通过使用 pass_context() 装饰器标记自己来请求传递上下文。 在这种情况下,上下文作为第一个参数传递。

上下文还可以携带可用于程序目的的程序指定对象。 这意味着您可以构建这样的脚本:

如果提供了对象,则每个上下文都会将该对象向前传递给其子对象,但在任何级别都可以覆盖上下文的对象。 要联系父母,可以使用 context.parent

除此之外,没有什么可以阻止应用程序修改全局状态,而不是向下传递对象。 例如,您可以只翻转一个全局 DEBUG 变量并完成它。


装饰命令

正如您在前面的示例中看到的,装饰器可以更改命令的调用方式。 在幕后实际发生的是,回调总是通过 Context.invoke() 方法调用,该方法自动正确调用命令(通过传递或不传递上下文)。

当您想编写自定义装饰器时,这非常有用。 例如,一种常见的模式是配置一个表示状态的对象,然后将其存储在上下文中,然后使用自定义装饰器查找此类最近的对象并将其作为第一个参数传递。

例如,pass_obj() 装饰器可以这样实现:

Context.invoke() 命令将自动以正确的方式调用该函数,因此该函数将被调用 f(ctx, obj)f(obj) 取决于它是否本身用 pass_context() 装饰。

这是一个非常强大的概念,可用于构建非常复杂的嵌套应用程序; 有关详细信息,请参阅 复杂应用程序


无命令组调用

默认情况下,除非传递子命令,否则不会调用组或多命令。 事实上,不提供命令会默认自动通过--help。 可以通过将 invoke_without_command=True 传递给组来更改此行为。 在这种情况下,回调总是被调用而不是显示帮助页面。 上下文对象还包括有关调用是否会转到子命令的信息。

例子:

以及它在实践中是如何工作的:


自定义多命令

除了使用 click.group(),您还可以构建自己的自定义多命令。 当您想支持从插件延迟加载的命令时,这很有用。

自定义多命令只需要实现一个列表和加载方法:

这些自定义类也可以与装饰器一起使用:


合并多个命令

除了实现自定义多命令之外,将多个命令合并到一个脚本中也很有趣。 虽然这通常不被推荐,因为它嵌套在另一个下面,但合并方法在某些情况下可能很有用,以获得更好的 shell 体验。

这种合并系统的默认实现是 CommandCollection 类。 它接受其他多命令的列表并使命令在同一级别可用。

用法示例:

以及它的样子:

如果命令存在于多个源中,则第一个源获胜。


多命令链

3.0 版中的新功能。


有时允许一次调用多个子命令很有用。 例如,如果您在熟悉 setup.py sdist bdist_wheel upload 命令链之前安装了 setuptools 包,该命令链在 upload 之前调用 dist 之前 bdist_wheel。 从 Click 3.0 开始,这很容易实现。 您所要做的就是将 chain=True 传递给您的多命令:

现在你可以像这样调用它:

使用多命令链接时,您只能让一个命令(最后一个)在参数上使用 nargs=-1。 也不可能在链接的多命令下嵌套多命令。 除此之外,它们的工作方式没有任何限制。 他们可以正常接受选项和参数。 选项和参数之间的顺序对于链式命令是有限制的。 目前只允许 --options argument 订单。

另一个注意事项:Context.invoked_subcommand 属性对于多命令有点无用,因为如果调用多个命令,它将给出 '*' 作为值。 这是必要的,因为子命令的处理一个接一个地发生,因此当回调触发时,将处理的确切子命令尚不可用。

笔记

目前无法嵌套链命令。 这将在 Click 的未来版本中修复。


多命令管道

3.0 版中的新功能。


多命令链的一个非常常见的用例是让一个命令处理上一个命令的结果。 有多种方式可以促进这一点。 最明显的方法是在上下文对象上存储一个值并从一个函数到另一个函数处理它。 这通过使用 pass_context() 装饰一个函数来工作,之后提供上下文对象并且子命令可以在那里存储其数据。

实现此目的的另一种方法是通过返回处理函数来设置管道。 可以这样想:当一个子命令被调用时,它会处理它的所有参数并提出如何进行处理的计划。 在这一点上,它然后返回一个处理函数并返回。

返回的函数去哪儿了? 链式多命令可以向 MultiCommand.resultcallback() 注册一个回调,该回调会遍历所有这些函数,然后调用它们。

为了使这更具体,请考虑以下示例:

一次性完成很多,所以让我们一步一步来完成。

  1. 第一件事是制作一个可链接的 group()。 除此之外,即使没有定义子命令,我们也会指示 Click 调用。 如果不这样做,则调用空管道将生成帮助页面,而不是运行结果回调。
  2. 接下来我们要做的是在我们的组上注册一个结果回调。 此回调将使用一个参数调用,该参数是所有子命令的所有返回值的列表,然后是与我们组本身相同的关键字参数。 这意味着我们可以在那里轻松访问输入文件,而无需使用上下文对象。
  3. 在这个结果回调中,我们为输入文件中的所有行创建一个迭代器,然后将这个迭代器传递给所有子命令返回的所有回调,最后我们将所有行打印到标准输出。

在那之后,我们可以根据需要注册任意数量的子命令,并且每个子命令都可以返回一个处理器函数来修改行流。

需要注意的一件重要事情是 Click 在每个回调运行后关闭上下文。 这意味着例如无法在 处理器 函数中访问文件类型,因为文件已在那里关闭。 这个限制不太可能改变,因为它会使资源处理变得更加复杂。 对于这种情况,建议不要使用文件类型并通过 open_file() 手动打开文件。

有关也改进管道处理的更复杂示例,请查看 Click 存储库中的 imagepipe 多命令链接演示 。 它实现了一个基于管道的图像编辑工具,该工具具有良好的管道内部结构。


覆盖默认值

默认情况下,参数的默认值是从定义时提供的 default 标志中提取的,但这不是唯一可以加载默认值的地方。 另一个地方是上下文中的 Context.default_map(一个字典)。 这允许从配置文件加载默认值以覆盖常规默认值。

如果您从另一个包中插入一些命令但对默认值不满意,这将非常有用。

每个子命令可以任意嵌套默认映射:

default_map = {
    "debug": True,  # default for a top level option
    "runserver": {"port": 5000}  # default for a subcommand
}

可以在调用脚本时提供默认映射,也可以在任何时候通过命令覆盖默认映射。 例如,顶级命令可以从配置文件加载默认值。

用法示例:

并在行动中:


上下文默认值

2.0 版中的新功能。


从 Click 2.0 开始,您不仅可以在调用脚本时覆盖上下文的默认值,还可以在声明命令的装饰器中覆盖默认值。 例如,前面的例子定义了一个自定义的 default_map,这现在也可以在装饰器中完成。

此示例与上一个示例执行相同的操作:

再举个例子:


命令返回值

3.0 版中的新功能。


Click 3.0 的新引入之一是完全支持命令回调的返回值。 这实现了以前难以实现的一系列功能。

本质上,任何命令回调现在都可以返回一个值。 这个返回值被冒泡到某些接收者。 在 多命令链 示例中已经展示了一个用例,其中已经证明链接的多命令可以具有处理所有返回值的回调。

在 Click 中处理命令返回值时,您需要了解以下内容:

  • 命令回调的返回值通常从 BaseCommand.invoke() 方法返回。 此规则的例外与 Groups 相关:
    • 在组中,返回值通常是调用的子命令的返回值。 此规则的唯一例外是,如果不带参数调用并且启用了 invoke_without_command,则返回值是组回调的返回值。
    • 如果为链接设置了一个组,则返回值是所有子命令结果的列表。
    • 可以通过 MultiCommand.result_callback 处理组的返回值。 这是在链模式下使用所有返回值的列表调用的,或者在非链式命令的情况下使用单个返回值调用。
  • 返回值从 Context.invoke()Context.forward() 方法冒泡出来。 这在您内部想要调用另一个命令的情况下很有用。
  • Click 对返回值没有任何硬性要求,并且本身不使用它们。 这允许将返回值用于自定义装饰器或工作流(如在多命令链示例中)。
  • 当 Click 脚本作为命令行应用程序(通过 BaseCommand.main())被调用时,返回值将被忽略,除非 standalone_mode 被禁用,在这种情况下它会冒泡。