首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有多个子命令的Python命令行工具的最佳体系结构

具有多个子命令的Python命令行工具的最佳体系结构
EN

Stack Overflow用户
提问于 2021-10-01 19:43:36
回答 3查看 750关注 0票数 1

我正在为一个项目开发一个命令行工具集。最后一个工具将支持许多子命令,如下所示

代码语言:javascript
复制
foo command1 [--option1 [value]?]*

所以可以有如下的子命令

代码语言:javascript
复制
foo create --option1 value --

foo make file1 --option2 --option3

该工具使用and解析库来处理命令行参数和帮助功能等。

一些额外的要求和制约因素:

  • 某些选项和功能对所有子命令都是相同的(例如解析YAML配置文件等)。
  • 有些子命令编写起来简单快捷,因为它们只是调用外部bash脚本。
  • 一些子命令将是复杂的,因此代码很长。
  • 对于基本工具的帮助应该是可用的,也可以用于一个单独的子命令: foo帮助可用的命令有: make、create、add、xyz foo帮助为make子命令提供详细信息。
  • 错误代码应该在各个子命令之间保持一致(就像“未找到文件”的错误代码一样)。

为了调试和为最小可行的版本进行自我包含的功能,我想开发一些自包含脚本和模块的子命令,如

代码语言:javascript
复制
make.py

可以导入到主foo.py脚本中,并在以后作为两个脚本调用

代码语言:javascript
复制
make.py --option1 value etc.

代码语言:javascript
复制
foo.py make --option1 value

现在,我的问题是:如何以最小的冗余来模块化这样一个复杂的CLI工具(例如,参数定义和解析应该只在一个组件中进行编码)?

选项1:将所有内容放入一个大脚本中,但这将变得很难管理。

选项2:为单个模块/文件(如make.pyadd.py)中的子命令开发功能;但这种功能必须保持可调用性(通过if __name__ == '__main__' ...)。

然后,可以将来自子命令模块的函数导入主脚本,解析器和子命令中的参数作为子解析器添加。

选项3:主脚本可以简单地将对子命令的调用格式化为子进程,如下所示

代码语言:javascript
复制
subprocess.run('./make.py {arguments}', shell=True, check=True, text=True)
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-10-03 00:53:22

谢谢你所有的建议!

我认为最优雅的方法是使用Typer并遵循以下方法:

https://typer.tiangolo.com/tutorial/subcommands/add-typer/

票数 0
EN

Stack Overflow用户

发布于 2021-10-01 22:23:20

我更习惯于回答有关numpyargparse细节的问题,但下面是我如何设想一个大型包的方法。

main.py

代码语言:javascript
复制
import submod1
# ....
sublist = [submod1, ...]
def make_parser(sublist):
    parser = argparse.ArgumentParser()
    # parser.add_argument('-f','--foo')  # main specific
    # I'd avoid positionals
    sp = parser.add_subparsers(dest='cmd', etc)
    splist=[]
    for md in sublist:
         sp1 = sp.add_parser(help='', parents=[md.parser])
         sp1.set_default(func=md.func)  # subparser func as shown in docs
         splist.append(sp1)
    return parser

如果名称为 == 'main':=make_parser(子列表) args = parser.parse_args() # print(args) #调试显示args.func(args) #。

submod1.py

导入argparse make_parser():解析器= argparse.ArgumentParser(add_help=False) #检查文档?Parser.add_argument(.)#可以在这里添加一个常见的父级返回解析器

代码语言:javascript
复制
parser.make_parser()

def func(args):
    # module specific 'main'

我确信这在很多方面是不完整的,因为我是在未经测试的情况下编写的。这是一个基本的子解析器定义,如文档所示,但使用parents导入子模块中定义的子解析器。parents也可以用于为子解析器定义公共参数;但是实用程序函数也同样有效。我认为parents在使用您无法访问的解析器时最有用;即。进口的。

parents实际上是通过引用(而不是值或副本)将操作从一个解析器复制到新的一个副本。这不是一个高度发达的工具,也有很多这样的工具,人们在那里遇到了问题。所以,不要试图过度扩展它。

票数 2
EN

Stack Overflow用户

发布于 2021-10-01 20:06:56

考虑使用指挥模式工厂方法模式

简而言之,创建一个名为Command的抽象类,并使每个命令从Command继承它自己的类。

示例:

代码语言:javascript
复制
class Command():

    def execute(self):
        raise NotImplementedError()


class Command1(Command):

    def __init__(self, *args):
        pass

    def execute(self):
        pass


class Command2(Command):

    def __init__(self, *args):
        pass

    def execute(self):
        pass

这将处理命令的执行。建造一个指挥工厂。

代码语言:javascript
复制
class CommandFactory():

    @staticmethod
    def create(command, *args):
        if command == 'command1':
            return Command1(args)
        elif command == 'command2':
            return Command2(args)

然后,您就可以使用一行执行命令:

代码语言:javascript
复制
CommandFactory.create(command, args).execute()
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69411257

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档