命令简介
我们已经了解了如何创建可能包含多个CLI 选项和CLI 参数的 CLI 程序。
但是,Typer 允许你创建包含多个命令(也称为子命令)的 CLI 程序。
例如,程序 git
具有多个命令。
git
的一个命令是 git push
。而 git push
又会使用自己的CLI 参数和CLI 选项。
例如
// The push command with no parameters
$ git push
---> 100%
// The push command with one CLI option --set-upstream and 2 CLI arguments
$ git push --set-upstream origin master
---> 100%
git
的另一个命令是 git pull
,它也有一些CLI 参数。
就好像同一个大型程序 git
在内部有几个小型程序一样。
提示
命令看起来与CLI 参数相同,它只是某个名称,前面没有 --
。但命令具有预定义的名称,用于将不同的功能集分组到同一个 CLI 应用程序中。
命令或子命令¶
通常将 CLI 程序称为“命令”。
但当其中一个程序具有子命令时,这些子命令也经常被称为“命令”。
请记住这一点,这样您就不会感到困惑。
这里我将使用CLI 应用程序或程序来指代您使用 Typer 在 Python 中构建的程序,并将命令用于指代程序的这些“子命令”之一。
显式应用程序¶
在使用多个命令/子命令创建 CLI 应用程序之前,我们需要了解如何创建显式的 typer.Typer()
应用程序。
在CLI 选项和CLI 参数教程中,您已经了解如何创建单个函数,然后将该函数传递给 typer.run()
。
例如
import typer
def main(name: str):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
但那实际上是一个快捷方式。在底层,Typer 将其转换为具有 typer.Typer()
的 CLI 应用程序并执行它。所有这些都在 typer.run()
内部。
还有一种更明确的方法来实现相同的功能
import typer
app = typer.Typer()
@app.command()
def main(name: str):
print(f"Hello {name}")
if __name__ == "__main__":
app()
当您使用 typer.run()
时,Typer 所做的与上述内容大致相同,它将
- 创建一个新的
typer.Typer()
“应用程序”。 - 使用您的函数创建一个新的“
命令
”。 - 使用“
app()
”调用与函数相同的“应用程序”。
@decorator
信息
Python 中的 @something
语法称为“装饰器”。
您将其放在函数的顶部。就像一顶漂亮的装饰帽(我想这就是这个术语的由来)。
“装饰器”获取下面的函数并对其进行处理。
在我们的例子中,这个装饰器告诉Typer,下面的函数是一个“命令
”。
使用 typer.run()
和创建显式应用程序这两种方式几乎可以实现相同的功能。
提示
如果您的用例仅使用 typer.run()
即可解决,那就很好,您不必创建显式的 app
并使用 @app.command()
等。
当您的应用程序需要额外功能时,您可能希望稍后这样做,但如果它现在不需要,那就很好。
如果您运行第二个示例,使用显式 app
,它的工作方式完全相同
// Without a CLI argument
$ python main.py
Usage: main.py [OPTIONS] NAME
Try "main.py --help" for help.
Error: Missing argument 'NAME'.
// With the NAME CLI argument
$ python main.py Camila
Hello Camila
// Asking for help
$ python main.py --help
Usage: main.py [OPTIONS] NAME
Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
--help Show this message and exit.
CLI 应用程序完成¶
这里有一个值得注意的小细节。
现在帮助显示两个新的CLI 选项
--install-completion
--show-completion
要获得 shell/tab 补全,有必要构建一个包,你和你的用户可以安装并直接调用。
因此,不要像运行 Python 脚本那样
$ python main.py
✨ Some magic here ✨
...应该像这样调用
$ magic-app
✨ Some magic here ✨
拥有这样的独立程序允许设置 shell/tab 补全。
能够创建这样的可安装包的第一步是使用显式的 typer.Typer()
应用程序。
稍后你可以了解创建独立 CLI 应用程序和构建包的所有过程。
但现在,知道你正在这条路上就足够了。 😎
具有多个命令的 CLI 应用程序¶
回到具有多个命令/子命令的 CLI 应用程序,Typer 允许创建具有多个命令的 CLI 应用程序。
现在你知道了如何创建显式的 typer.Typer()
应用程序并添加一个命令,让我们看看如何添加多个命令。
假设我们有一个 CLI 应用程序来管理用户。
我们将有一个命令来创建
用户,另一个命令来删除
用户。
首先,我们假设它只能创建和删除一个预定义的单个用户
import typer
app = typer.Typer()
@app.command()
def create():
print("Creating user: Hiro Hamada")
@app.command()
def delete():
print("Deleting user: Hiro Hamada")
if __name__ == "__main__":
app()
现在我们有一个具有 2 个命令的 CLI 应用程序,create
和 delete
// Check the help
$ python main.py --help
Usage: main.py [OPTIONS] COMMAND [ARGS]...
Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
--help Show this message and exit.
Commands:
create
delete
// Test them
$ python main.py create
Creating user: Hiro Hamada
$ python main.py delete
Deleting user: Hiro Hamada
// Now we have 2 commands! 🎉
请注意,帮助文本现在显示 2 个命令:create
和 delete
。
提示
默认情况下,命令的名称是从函数名称生成的。
如果没有给定命令,则显示帮助消息¶
默认情况下,我们需要指定 --help
来获取命令的帮助页面。
但是,在定义 typer.Typer()
应用程序时设置 no_args_is_help=True
,则每当没有给出参数时都将显示帮助函数
import typer
app = typer.Typer(no_args_is_help=True)
@app.command()
def create():
print("Creating user: Hiro Hamada")
@app.command()
def delete():
print("Deleting user: Hiro Hamada")
if __name__ == "__main__":
app()
现在我们可以运行此命令
// Check the help without having to type --help
$ python main.py
Usage: main.py [OPTIONS] COMMAND [ARGS]...
Options:
--install-completion Install completion for the current shell.
--show-completion Show completion for the current shell, to copy it or customize the installation.
--help Show this message and exit.
Commands:
create
delete
单击组¶
如果你来自 Click,带有子命令的 typer.Typer
应用程序或多或少相当于Click 组。
技术细节
typer.Typer
应用程序不是 Click 组,但它提供了同等的功能。并且在调用它时创建一个 Click 组。
它不是直接的组,因为Typer 不会修改代码中的函数以将其转换为另一种类型的对象,它只注册它们。
装饰器技术细节¶
当你使用 @app.command()
时,装饰器下的函数在 Typer 应用程序中注册,然后由应用程序在后面使用。
但是 Typer 不会修改函数本身,函数保持原样。
这意味着,如果你的函数足够简单,你可以不用 typer.Option()
或 typer.Argument()
来创建它,你可以将同一个函数用于 Typer 应用程序和 FastAPI 应用程序,将两个装饰器放在顶部,或者使用类似的技巧。
Click 技术细节
这种行为与 Click 的设计不同。
在 Click 中,当你添加 @click.command()
装饰器时,它实际上会修改下面的函数并用一个对象替换它。