将pydantic定义的数据模型转换为cli工具

pydantic-cli的Python项目详细描述


pydantic命令行工具接口

将pydantic定义的数据模型转换为cli工具!

功能

  1. 构建在Pydantic
  2. 之上的模式驱动接口
  3. 验证在pydantic验证模型定义的单个位置执行
  4. cli解析仅在结构上验证是否提供了参数或可选参数
  5. 清除cli与应用程序代码之间的接口
  6. 易于测试(由于上述原因)

快速启动

要创建一个命令行工具,该工具将输入文件和要处理的最大记录数作为位置参数:

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_exitto_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 codeprogram 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)

子类

通过创建容器SubParserdict并调用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文件添加加载

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
cassandra 2.0 SparkSQL不支持java。util。日期   java如何在Liferay中将值从输入发送到AutoLogin类   java创建列表并获取每个列表的最后一项   java堆栈被覆盖了   java Velocity#foreach,带XmlTool节点列表,带文本节点   java为什么不使用BitmapFactory。解码文件返回空值?   java如何将git存储url连接到本地存储库,以便每次都获得更新的代码?   java为什么Maven找不到这个jar   java JavaCV画布保持空白   java使用datePickerDialog当我选择日期时,月的值出乎意料   Solaris 9上的Java 1.6.0_45返回重新定位错误“symbol\u fmodf:未找到引用的符号”   java如何高效地返回部分文件?   JOptionPane的Java问题