如何使用CLI检查python脚本中的输入参数?

yzuktlbb  于 2022-12-10  发布在  Python
关注(0)|答案(3)|浏览(139)

我正在编写一个学习Python的小脚本。该脚本打印了一个N个玩家的国际象棋锦标赛表。它有一个简单的CLI,带有一个参数N。现在我尝试以下方法:

import argparse

def parse_args(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Tournament tables")
    parser.add_argument('N', help="number of players (2 at least)", type=int)
    args = parser.parse_args(argv)
    if args.N < 2:
        parser.error("N must be 2 at least")
    return args.N

def main(n: int) -> None:
    print(F"Here will be the table for {n} players")

if __name__ == '__main__':
    main(parse_args())

但这似乎有一个缺陷。函数main不检查n的无效输入(因为这是CLI解析器的工作)。因此,如果有人直接从另一个模块(例如测试器)调用main,他可能会用0来调用它,程序很可能会崩溃。
我应该如何妥善处理这个问题?
我正在考虑几种可能的方法,但不能肯定哪种是最好的。
1.在main中添加一个适当的值检查和错误处理。这个选项在我看来很难看,因为它违反了DRY原则,并迫使main将CLI的工作增加了一倍。
1.只需证明main必须只取n〉= 2,否则它的行为是不可预测的。可以合并对main添加Assert检查,如下所示:
assert n >= 2, "n must be 2 or more"
1.也许这样一个函数根本不应该是外部的?所以整个选择的习惯用法是错误的,脚本的入口点应该以另一种方式重写。
1.你说什么?

chy5wohz

chy5wohz1#

你可以让main做所有的检查,如果有什么不对的地方,就引发ArgumentError。然后捕获这个异常并将其转发给解析器显示。大致如下:

import argparse

def run_with_args(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Tournament tables")
    parser.add_argument('N', help="number of players (2 at least)", type=int)
    args = parser.parse_args(argv)
    try:
        main(args.N)
    except argparse.ArgumentError as ex:
        parser.error(str(ex))

def main(n: int) -> None:
    if N < 2:
        raise argparse.ArgumentError("N must be 2 at least")
    print(F"Here will be the table for {n} players")

if __name__ == '__main__':
    run_with_args()

如果您不想向main的库用户公开argparse.ArgumentError,也可以创建一个自定义异常类型来代替它。

hjzp0vay

hjzp0vay2#

当需要测试函数/CLI时,运行argparse的一种常见方法是让main函数获取sys.argv列表,然后从main中调用parse_args,如下所示:

  • 参数py*
import argparse

def parse_args(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Tournament tables", prog="prog")
    parser.add_argument("N", help="number of players (2 at least)", type=int)
    args = parser.parse_args(argv)
    if args.N < 2:
        parser.error("N must be 2 at least")
    return args

def main(argv: list[str] | None = None) -> None:
    args = parse_args(argv)
    print(f"Here will be the table for {args.N} players")

if __name__ == "__main__":
    main()

这样,测试就可以使用假设的CLI调用main:

  • 测试_主文件.py*
import pytest
from arg import main

def test_main(capsys):
    with pytest.raises(SystemExit):
        main(["0"])
    out, err = capsys.readouterr()
    assert err.splitlines()[-1] == "prog: error: N must be 2 at least"
hs1rzwqc

hs1rzwqc3#

我一直在使用Pydantic来强制运行时的数据类型化,* 在我的代码中 *。
它是一个非常健壮、使用非常广泛的库,而且速度非常快,因为它更像是一个数据接收验证器,而不是类型检查器。
你可以编写如下的调用,如何调用main完全取决于你:直接调用,argparse,单击...

class ParamChecker(BaseModel):
    n : int

    @validator('n')
    def n2(cls, v):
        if v <2:
            raise ValueError('must be 2+')
        return v

def main(n: int) -> None:
    params = ParamChecker(**locals())

Pydantic还可以为您提供信息丰富的错误消息,如果不是真正的最终用户友好的话。

相关问题