如何通过从 ArgumentParser 获取命令和子命令容器来实现更加模块化的 CLI?

问题描述 投票:0回答:1

我有一个类似 git 的大型 CLI,其中包含许多命令、子命令和参数。但是,大多数 Argparse 教程仅显示使用单个文件的简单 CLI。这种方法有效,但导致模块庞大且难以维护。通过为每个命令创建一个类,传入命令容器(由

parser.add_subparsers
创建),并将命令添加到命令容器中,我部分成功地使代码库更加模块化。

这非常有效,直到我遇到一个包含很多子命令的命令。虽然我可以采用相同的方法并将子命令容器 (

command.add_subparsers
) 传递给每个子命令,但似乎更好的方法是反转控制并让每个命令和子命令 get (或创建和获取)各自的容器。这应该很简单,因为每个子命令都知道其父命令。例如,如果我有以下结构:

command1  
command2  
  subcommand_A
  subcommand_B
command3  

我想添加 sucommand_B 执行以下操作:

parser.get_container(command="command2", subcommand="subcommand_B").add_parser(subcommand_B)

这似乎是可能的,但不推荐,因为容器深埋在 ArgumentParser 的私有属性中。

有没有一种好方法可以从解析器(即 ArgumentParser)中检索命令和子命令容器?如果没有,我还能如何使我的命令和子命令更加模块化?

argparse
1个回答
0
投票

我已经回答了很多关于

argparse
的问题,并尝试跟踪所有相关的错误/问题。尚未讨论多文件组织。某些第三方软件包可能会尝试扩展
arpgarse
。几年前,我通过 pypi 存储库上的
argparse
包发现了
plac

ipython/jupyter
还使用(或至少习惯于)它自己的
arpgarse
变体,从
config
文件构建参数。并且
magics
使用类似 argparse 的解析器。

你看过

argparse.py
代码吗?它使用类、几个更抽象的“容器”类以及用户创建的
parser
action
类编写得很好。许多属性是“公共”的,但一些有用的属性也是“半私有”的(在Python的松散意义上)。

我会尝试用一个简单的案例来说明:

In [1]: import argparse

In [2]: parser = argparse.ArgumentParser()    
In [3]: a1 = parser.add_argument('-f','--foo')    
In [4]: sp = parser.add_subparsers(dest='cmd')    
In [5]: sp1 = sp.add_parser('cmd1')    
In [6]: a2 = sp1.add_argument('-b','--bar')

每个命令返回一个对象

解析器本身:

In [7]: parser
Out[7]: ArgumentParser(prog='ipykernel_launcher.py', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

._actions
是一个有用的列表,其中包含我们创建的所有
Action
子类对象(包括自动帮助)。

In [8]: parser._actions
Out[8]: 
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, required=False, help='show this help message and exit', metavar=None),
 _StoreAction(option_strings=['-f', '--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None),
 _SubParsersAction(option_strings=[], dest='cmd', nargs='A...', const=None, default=None, type=None, choices={'cmd1': ArgumentParser(prog='ipykernel_launcher.py cmd1', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)}, required=False, help=None, metavar=None)]

请注意,该列表包括我们分配给

subparsers
sp
操作。对于
parser
来说,
sp
只是另一个位置参数。它的所有特殊行为都嵌入在它的子类定义中。

In [9]: sp
Out[9]: _SubParsersAction(option_strings=[], dest='cmd', nargs='A...', const=None, default=None, type=None, choices={'cmd1': ArgumentParser(prog='ipykernel_launcher.py cmd1', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)}, required=False, help=None, metavar=None)

子解析器是另一个

ArgumentParser
对象,有自己的
_actions
:

In [10]: sp1
Out[10]: ArgumentParser(prog='ipykernel_launcher.py cmd1', usage=None, description=None, formatter_class=<class 'argparse.HelpFormatter'>, conflict_handler='error', add_help=True)

In [11]: sp1._actions
Out[11]: 
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, required=False, help='show this help message and exit', metavar=None),
 _StoreAction(option_strings=['-b', '--bar'], dest='bar', nargs=None, const=None, default=None, type=None, choices=None, required=False, help=None, metavar=None)]

因此,您可以通过查看属性(带或不带“_”标头的属性)来发现这些对象彼此“了解”的所有内容。

为了帮助格式化,所有操作都是

Action_group
的一部分。但这是在解析过程中使用的另一层组织(除了相互排斥的组)。

总而言之,

argparse.py
中没有任何旨在促进多文件组织的内容。但我想可以利用它的阶级组织来使这变得更容易。

© www.soinside.com 2019 - 2024. All rights reserved.