将pydantic定义的数据模型转换为cli工具
pydantic-cli的Python项目详细描述
pydantic命令行工具接口
将pydantic定义的数据模型转换为cli工具!
功能
- 构建在Pydantic 之上的模式驱动接口
- 验证在pydantic验证模型定义的单个位置执行
- cli解析仅在结构上验证是否提供了参数或可选参数
- 清除cli与应用程序代码之间的接口
- 易于测试(由于上述原因)
快速启动
要创建一个命令行工具,该工具将输入文件和要处理的最大记录数作为位置参数:
my-tool /path/to/file.txt 1234
这需要两个组件。
- 创建类型为
T
的pydantic数据模型
- 编写一个函数,该函数采用^ {CD1}}的实例,并返回退出代码(例如,成功0,失败非零)。
- 将
T
传递到to_runner
函数或run_and_exit
下面是明确的例子。
importsysfrompydanticimportBaseModelfrompydantic_cliimportrun_and_exit,to_runnerclassMinOptions(BaseModel):input_file:strmax_records:intdefexample_runner(opts:MinOptions)->int:print(f"Mock example running with options {opts}")return0if__name__=='__main__':# to_runner will return a function that takes the args list to run and # will return an integer exit codesys.exit(to_runner(MinOptions,example_runner,version='0.1.0')(sys.argv[1:]))
或者隐式使用sys.argv[1:]
,call可以利用run_and_exit
(to_runner
对于测试也很有用)。
if__name__=='__main__':run_and_exit(MinOptions,example_runner,description="My Tool Description",version='0.1.0')
如果数据模型具有默认值,则命令行参数的be optional和cli参数将以“---”作为前缀。
例如:
frompydanticimportBaseModelfrompydantic_cliimportrun_and_exitclassMinOptions(BaseModel):input_file:strmax_records:int=10defexample_runner(opts:MinOptions)->int:print(f"Mock example running with options {opts}")return0if__name__=='__main__':run_and_exit(MinOptions,example_runner,description="My Tool Description",version='0.1.0')
将使用my-tool /path/to/input.txt --max_records 1234
my-tool /path/to/input.txt --max_records 1234
命令行接口的--max_records
是可选的。
warning:必须显式地传递布尔值(例如,--run_training True
)
--help
是非常小的(由于缺少元数据),但是,详细命名的参数通常足以传达命令行接口的意图。
对于cli参数的定制,例如最大记录数是-m 1234
,在上面的示例中,有两种方法。
- 第一个是“quick”方法,它是对pydantic数据模型的
Config
的一个小更改。 - 第二个“schema”方法是在^{
} model in Pydantic 中定义元数据。
定制的快速模型
我们将把用法从my-tool /path/to/file.txt 1234
改为my-tool /path/to/file.txt -m 1234
。
这只需要将CLI_EXTRA_OPTIONS
添加到pydanticConfig
。
frompydanticimportBaseModelclassMinOptions(BaseModel):classConfig:CLI_EXTRA_OPTIONS={'max_records':('-m',)}input_file:strmax_records:int=10
也可以重写“long”参数。但是,注意,这开始在架构的顶部添加一个新的间接层。(例如,“max_records”到“-max records”)可能有用,也可能不有用。
frompydanticimportBaseModelclassMinOptions(BaseModel):classConfig:CLI_EXTRA_OPTIONS={'max_records':('-m','--max-records')}input_file:strmax_records:int=10
模式方法
frompydanticimportBaseModel,SchemaclassOptions(BaseModel):classConfig:validate_all=Truevalidate_assignment=Trueinput_file:str=Schema(...,# this implicitly means required=Truetitle="Input File",description="Path to the input file",required=True,extras={"cli":('-f','--input-file')})max_records:int=Schema(123,title="Max Records",description="Max number of records to process",gt=0,extras={'cli':('-m','--max-records')})
挂接到cli执行中
- 异常处理程序
- 结语处理者
这两种情况都可以通过向running/execution方法传递函数来定制。
异常处理程序应处理对STDRR的任何日志记录或写入,以及将特定异常映射到非零整数退出代码。
例如:
importsysfrompydantic_cliimportrun_and_exitdefcustom_exception_handler(ex)->int:exception_map=dict(ValueError=3,IOError=7)sys.stderr.write(str(ex))exit_code=exception_map.get(ex.__class__,1)returnexit_codeif__name__=='__main__':run_and_exit(MinOptions,example_runner,exception_handler=custom_exception_handler)
类似地,可以调用执行后钩子。这个函数是Callable[[int, float], None]
,即秒中的exit code
和program runtime
作为输入。
importsysfrompydantic_cliimportrun_and_exitdefcustom_epilogue_handler(exit_code:int,run_time_sec:float):m="Success"ifexit_codeelse"Failed"msg=f"Completed running ({m}) in {run_time_sec:.2f} sec"print(msg)if__name__=='__main__':run_and_exit(MinOptions,example_runner,epilogue_handler=custom_epilogue_handler)
子类
通过创建容器SubParser
dict并调用run_sp_and_exit
importtypingasTfrompydanticimportBaseModelfrompydantic.schemaimportUrlStrfrompydantic_cli.examplesimportConfigDefaultsfrompydantic_cliimportrun_sp_and_exit,SubParserclassAlphaOptions(BaseModel):classConfig(ConfigDefaults):CLI_EXTRA_OPTIONS={'max_records':('-m','--max-records')}input_file:strmax_records:int=10classBetaOptions(BaseModel):classConfig(ConfigDefaults):CLI_EXTRA_OPTIONS={'url':('-u','--url'),'num_retries':('-n','--num-retries')}url:UrlStrnum_retries:int=3defprinter_runner(opts:T.Any):print(f"Mock example running with {opts}")return0defto_runner(sx):defexample_runner(opts)->int:print(f"Mock {sx} example running with {opts}")return0returnexample_runnerdefto_subparser_example():return{'alpha':SubParser(AlphaOptions,to_runner("Alpha"),"Alpha SP Description"),'beta':SubParser(BetaOptions,to_runner("Beta"),"Beta SP Description")}if__name__=="__main__":run_sp_and_exit(to_subparser_example(),description=__doc__,version='0.1.0')
限制
- 目前只支持平面“简单”类型(例如float、int、strings、boolean)。当前不支持
List[T]
或嵌套的dict。 - 利用引擎盖下的argparse和argparse是一个有点棘手的api来构建。
改善
- 在“帮助”中键入更好的说明
- 更好地交流“帮助”中所需的“选项”
- 从json文件添加加载