版本 CLI 选项,is_eager
你可以使用回调来实现一个 --version
CLI 选项。
它将显示你的 CLI 程序的版本,然后终止它。甚至在处理任何其他 CLI 参数 之前。
--version
的第一个版本¶
让我们看看它的第一个版本可能是什么样子
from typing import Optional
import typer
from typing_extensions import Annotated
__version__ = "0.1.0"
def version_callback(value: bool):
if value:
print(f"Awesome CLI Version: {__version__}")
raise typer.Exit()
def main(
name: Annotated[str, typer.Option()] = "World",
version: Annotated[
Optional[bool], typer.Option("--version", callback=version_callback)
] = None,
):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
提示
如果可能,最好使用 Annotated
版本。
from typing import Optional
import typer
__version__ = "0.1.0"
def version_callback(value: bool):
if value:
print(f"Awesome CLI Version: {__version__}")
raise typer.Exit()
def main(
name: str = typer.Option("World"),
version: Optional[bool] = typer.Option(
None, "--version", callback=version_callback
),
):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
提示
请注意,我们不必获取 typer.Context
并检查 ctx.resilient_parsing
是否可以完成,因为我们仅在传递 --version
时才打印和修改程序,否则,不会从回调中打印或更改任何内容。
如果传递了 --version
CLI 选项,我们将在回调中获得一个值 True
。
然后,我们可以打印版本并引发 typer.Exit()
以确保在执行任何其他操作之前终止程序。
我们还声明显式的 CLI 选项 名称 --version
,因为我们不想要自动的 --no-version
,它看起来很奇怪。
检查一下
$ python main.py --help
// We get a --version, and don't get an awkward --no-version 🎉
Usage: main.py [OPTIONS]
Options:
--version
--name TEXT
--help Show this message and exit.
// We can call it normally
$ python main.py --name Camila
Hello Camila
// And we can get the version
$ python main.py --version
Awesome CLI Version: 0.1.0
// Because we exit in the callback, we don't get a "Hello World" message after the version 🚀
之前的参数和 is_eager
¶
但现在假设我们之前在 --version 之前声明的 --name
CLI 选项 是必需的,并且它有一个可以退出程序的回调
from typing import Optional
import typer
from typing_extensions import Annotated
__version__ = "0.1.0"
def version_callback(value: bool):
if value:
print(f"Awesome CLI Version: {__version__}")
raise typer.Exit()
def name_callback(name: str):
if name != "Camila":
raise typer.BadParameter("Only Camila is allowed")
def main(
name: Annotated[str, typer.Option(callback=name_callback)],
version: Annotated[
Optional[bool], typer.Option("--version", callback=version_callback)
] = None,
):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
提示
如果可能,最好使用 Annotated
版本。
from typing import Optional
import typer
__version__ = "0.1.0"
def version_callback(value: bool):
if value:
print(f"Awesome CLI Version: {__version__}")
raise typer.Exit()
def name_callback(name: str):
if name != "Camila":
raise typer.BadParameter("Only Camila is allowed")
def main(
name: str = typer.Option(..., callback=name_callback),
version: Optional[bool] = typer.Option(
None, "--version", callback=version_callback
),
):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
那么我们的 CLI 程序在某些情况下可能无法按预期工作,因为它现在是,因为如果我们在 --name 之后使用 --version,那么 --name 的回调将被提前处理,并且我们可以得到它的错误
$ python main.py --name Rick --version
Only Camila is allowed
Aborted!
提示
我们不必在 name_callback() 中检查 ctx.resilient_parsing 以便完成工作,因为我们没有使用 typer.echo(),而是引发了一个 typer.BadParameter。
技术细节
typer.BadParameter
将错误打印到“标准错误”,而不是“标准输出”,并且因为完成系统只从“标准输出”读取,所以它不会破坏完成。
信息
如果你需要复习一下什么是“标准输出”和“标准错误”,请查看 打印和颜色:“标准输出”和“标准错误” 中的部分。
使用 is_eager
修复¶
对于这些情况,我们可以使用 is_eager=True
标记一个CLI 参数(一个CLI 选项或CLI 参数)。
这将告诉 Typer(实际上是 Click)它应该在其他参数之前处理这个CLI 参数
from typing import Optional
import typer
from typing_extensions import Annotated
__version__ = "0.1.0"
def version_callback(value: bool):
if value:
print(f"Awesome CLI Version: {__version__}")
raise typer.Exit()
def name_callback(name: str):
if name != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return name
def main(
name: Annotated[str, typer.Option(callback=name_callback)],
version: Annotated[
Optional[bool],
typer.Option("--version", callback=version_callback, is_eager=True),
] = None,
):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
提示
如果可能,最好使用 Annotated
版本。
from typing import Optional
import typer
__version__ = "0.1.0"
def version_callback(value: bool):
if value:
print(f"Awesome CLI Version: {__version__}")
raise typer.Exit()
def name_callback(name: str):
if name != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return name
def main(
name: str = typer.Option(..., callback=name_callback),
version: Optional[bool] = typer.Option(
None, "--version", callback=version_callback, is_eager=True
),
):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
检查一下
$ python main.py --name Rick --version
// Now we only get the version, and the name is not used
Awesome CLI Version: 0.1.0