CLI 选项回调和上下文
在某些情况下,你可能希望为特定CLI 参数(对于CLI 选项或CLI 参数)有一些自定义逻辑,该逻辑使用从终端接收的值执行。
在这些情况下,你可以使用CLI 参数回调函数。
验证CLI 参数¶
例如,你可以在执行其余代码之前进行一些验证。
from typing import Optional
import typer
from typing_extensions import Annotated
def name_callback(value: str):
if value != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return value
def main(name: Annotated[Optional[str], typer.Option(callback=name_callback)] = None):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
提示
如果可能,最好使用Annotated
版本。
from typing import Optional
import typer
def name_callback(value: str):
if value != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return value
def main(name: Optional[str] = typer.Option(default=None, callback=name_callback)):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
此处,您使用关键字参数 callback
将函数传递给 typer.Option()
或 typer.Argument()
。
该函数接收来自命令行中的值。它可以对该值执行任何操作,然后返回值。
在此情况下,如果 --name
不为 Camila
,我们将引发 typer.BadParameter()
异常。
BadParameter
异常是特殊的,它会显示生成该异常的参数的错误。
检查它
$ python main.py --name Camila
Hello Camila
$ python main.py --name Rick
Usage: main.py [OPTIONS]
// We get the error from the callback
Error: Invalid value for '--name': Only Camila is allowed
处理自动补全¶
对于回调和自动补全,需要一些小的特殊处理,这一点需要注意。
但首先,让我们在您的 shell(Bash、Zsh、Fish 或 PowerShell)中使用自动补全。
在为自己的 Python 包安装自动补全后,当您使用 CLI 程序并开始使用 --
添加一个 CLI 选项,然后按 TAB 时,您的 shell 将向您显示可用的 CLI 选项(对于 CLI 参数 等也是如此)。
要使用上一个脚本快速检查它,请使用 typer
命令
// Hit the TAB key in your keyboard below where you see the: [TAB]
$ typer ./main.py [TAB][TAB]
// Depending on your terminal/shell you will get some completion like this ✨
run -- Run the provided Typer app.
utils -- Extra utility commands for Typer apps.
// Then try with "run" and --help
$ typer ./main.py run --help
// You get a help text with your CLI options as you normally would
Usage: typer run [OPTIONS]
Run the provided Typer app.
Options:
--name TEXT [required]
--help Show this message and exit.
// Then try completion with your program
$ typer ./main.py run --[TAB][TAB]
// You get completion for CLI options
--help -- Show this message and exit.
--name
// And you can run it as if it was with Python directly
$ typer ./main.py run --name Camila
Hello Camila
shell 自动补全的工作原理¶
其内部工作原理是,shell/终端将使用一些特殊环境变量(保存当前 CLI 参数 等)调用您的 CLI 程序,并且您的 CLI 程序将打印一些特殊值,shell 将使用这些值来显示自动补全。所有这些都由 Typer 在后台为您处理。
但主要的重点是,它全部基于 shell 读取的由您的程序打印的值。
在回调中中断自动补全¶
假设在回调运行时,我们希望显示一条消息,说明正在验证名称
from typing import Optional
import typer
from typing_extensions import Annotated
def name_callback(value: str):
print("Validating name")
if value != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return value
def main(name: Annotated[Optional[str], typer.Option(callback=name_callback)] = None):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
提示
如果可能,最好使用Annotated
版本。
from typing import Optional
import typer
def name_callback(value: str):
print("Validating name")
if value != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return value
def main(name: Optional[str] = typer.Option(default=None, callback=name_callback)):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
并且由于在 shell 调用您的程序请求自动补全时将调用回调,因此该消息 "Validating name"
将被打印,并且它将中断自动补全。
它看起来像
// Run it normally
$ typer ./main.py run --name Camila
// See the extra message "Validating name"
Validating name
Hello Camila
$ typer ./main.py run --[TAB][TAB]
// Some weird broken error message ⛔️
(eval):1: command not found: Validating
rutyper ./main.pyed Typer app.
修复补全 - 使用 Context
¶
当你创建一个 Typer 应用程序时,它在内部使用 Click。
每个 Click 应用程序都有一个特殊对象,称为 "上下文",它通常是隐藏的。
但是,你可以通过声明类型为 typer.Context
的函数参数来访问上下文。
"上下文" 有一些关于当前程序执行的附加数据
from typing import Optional
import typer
from typing_extensions import Annotated
def name_callback(ctx: typer.Context, value: str):
if ctx.resilient_parsing:
return
print("Validating name")
if value != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return value
def main(name: Annotated[Optional[str], typer.Option(callback=name_callback)] = None):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
提示
如果可能,最好使用Annotated
版本。
from typing import Optional
import typer
def name_callback(ctx: typer.Context, value: str):
if ctx.resilient_parsing:
return
print("Validating name")
if value != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return value
def main(name: Optional[str] = typer.Option(default=None, callback=name_callback)):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
在处理补全时,ctx.resilient_parsing
将为 True
,因此你可以直接返回而不打印任何其他内容。
但在正常调用程序时,它将为 False
。因此,你可以继续执行之前的代码。
修复补全只需要这些。🚀
检查它
$ typer ./main.py run --[TAB][TAB]
// Now it works correctly 🎉
--help -- Show this message and exit.
--name
// And you can call it normally
$ typer ./main.py run --name Camila
Validating name
Hello Camila
使用 CallbackParam
对象¶
通过声明具有其值函数参数,你可以访问 typer.Context
的方式相同,你可以声明另一个类型为 typer.CallbackParam
的函数参数以获取特定的 Click Parameter
对象。
from typing import Optional
import typer
from typing_extensions import Annotated
def name_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):
if ctx.resilient_parsing:
return
print(f"Validating param: {param.name}")
if value != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return value
def main(name: Annotated[Optional[str], typer.Option(callback=name_callback)] = None):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
提示
如果可能,最好使用Annotated
版本。
from typing import Optional
import typer
def name_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):
if ctx.resilient_parsing:
return
print(f"Validating param: {param.name}")
if value != "Camila":
raise typer.BadParameter("Only Camila is allowed")
return value
def main(name: Optional[str] = typer.Option(default=None, callback=name_callback)):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
这可能不太常见,但如果你需要,可以这样做。
例如,如果你有一个可以被多个CLI 参数使用的回调,这样回调就可以知道每次是哪个参数。
检查它
$ python main.py --name Camila
Validating param: name
Hello Camila
技术细节¶
因为你根据标准 Python 类型注释在回调函数中获取相关数据,所以你可以免费在编辑器中获得类型检查和自动补全。
Typer 将确保你获得所需的函数参数。
你不必担心它们的名字、顺序等。
因为它基于标准 Python 类型,所以它 "只需工作"。✨
Click 的 Parameter
¶
typer.CallbackParam
实际上只是 Click 的 Parameter
的一个子类,因此你可以在编辑器中获得所有正确的补全。
带有类型注释的回调¶
只需声明每个类型的函数参数,即可获取 typer.Context
和 typer.CallbackParam
。
顺序无关紧要,函数参数的名称无关紧要。
您还可以仅获取 typer.CallbackParam
而不获取 typer.Context
,反之亦然,它仍然有效。
value
函数参数¶
回调中的 value
函数参数也可以具有任何名称(例如 lastname
)和任何类型,但它应该具有与主函数中相同的类型注释,因为这是它将接收的内容。
也可以不声明其类型。它仍然有效。
并且完全可以不声明 value
参数,例如,仅获取 typer.Context
。这也将有效。