同时解析python中的多个子命令或以其他方式对解析的参数进行分组

Posted

技术标签:

【中文标题】同时解析python中的多个子命令或以其他方式对解析的参数进行分组【英文标题】:Parse multiple subcommands in python simultaneously or other way to group parsed arguments 【发布时间】:2015-10-12 03:19:09 【问题描述】:

我正在将 Bash shell 安装程序实用程序转换为 Python 2.7,并且需要实现复杂的 CLI,以便能够解析数十个参数(可能高达 ~150)。除了十几个通用部署选项之外,这些是 Puppet 类变量的名称,这些选项在 shell 版本中可用。

然而,在我开始添加更多变量之后,我面临着几个挑战: 1. 我需要将参数分组到单独的字典中,以便将部署选项与 Puppet 变量分开。如果它们被扔到同一个桶中,那么我将不得不编写一些逻辑来对它们进行排序,可能会重命名参数,然后字典合并不会是微不足道的。 2. 可能存在同名但属于不同 Puppet 类的变量,所以我认为子命令可以让我过滤去哪里,避免名称冲突。

目前我已经通过简单地添加多个解析器来实现参数解析:

parser = argparse.ArgumentParser(description='deployment parameters.')
env_select = parser.add_argument_group(None, 'Environment selection')
env_select.add_argument('-c', '--client_id',  help='Client name to use.')
env_select.add_argument('-e', '--environment', help='Environment name to use.')
setup_type = parser.add_argument_group(None, 'What kind of setup should be done:')
setup_type.add_argument('-i', '--install', choices=ANSWERS, metavar='', action=StoreBool, help='Yy/Nn Do normal install and configuration')
# MORE setup options
...
args, unk = parser.parse_known_args()
config['deploy_cfg'].update(args.__dict__)

pup_class1_parser = argparse.ArgumentParser(description=None)
pup_class1 = pup_class1_parser.add_argument_group(None, 'Puppet variables')
pup_class1.add_argument('--ad_domain', help='AD/LDAP domain name.')
pup_class1.add_argument('--ad_host', help='AD/LDAP server name.')
# Rest of the parameters

args, unk = pup_class1_parser.parse_known_args()
config['pup_class1'] = dict()
config['pup_class1'].update(args.__dict__)
# Same for class2, class3 and so on.

这种方法无法解决问题 2 的问题。此外,第一个解析器使用“-h”选项,其余参数未显示在帮助中。

我曾尝试使用example selected as an answer,但我无法同时使用这两个命令。

## This function takes the 'extra' attribute from global namespace and re-parses it to create separate namespaces for all other chained commands.
def parse_extra (parser, namespace):
  namespaces = []
  extra = namespace.extra
  while extra:
    n = parser.parse_args(extra)
    extra = n.extra
    namespaces.append(n)

  return namespaces

pp = pprint.PrettyPrinter(indent=4)

argparser=argparse.ArgumentParser()
subparsers = argparser.add_subparsers(help='sub-command help', dest='subparser_name')

parser_a = subparsers.add_parser('command_a', help = "command_a help")
## Setup options for parser_a
parser_a.add_argument('--opt_a1', help='option a1')
parser_a.add_argument('--opt_a2', help='option a2')

parser_b = subparsers.add_parser('command_b', help = "command_b help")
## Setup options for parser_a
parser_b.add_argument('--opt_b1', help='option b1')
parser_b.add_argument('--opt_b2', help='option b2')


## Add nargs="*" for zero or more other commands
argparser.add_argument('extra', nargs = "*", help = 'Other commands')

namespace = argparser.parse_args()
pp.pprint(namespace)
extra_namespaces = parse_extra( argparser, namespace )
pp.pprint(extra_namespaces)

结果我:

$ python argtest.py command_b --opt_b1 b1 --opt_b2 b2 command_a --opt_a1 a1
usage: argtest.py [-h] command_a,command_b ... [extra [extra ...]]
argtest.py: error: unrecognized arguments: command_a --opt_a1 a1

当我尝试用两个子解析器定义父时,结果相同。

问题

    我能否以某种方式使用 parser.add_argument_group 进行参数解析,还是仅用于帮助打印输出中的分组?它将解决问题 1,而不会丢失帮助的副作用。将其作为 parse_known_args(namespace=argument_group) 传递(如果我正确地记得我的实验)会获取所有变量(没关系),但也会在结果 dict 中获取所有 Python 对象内容(这对 hieradata YAML 不利) 我在第二个示例中缺少什么以允许使用多个子命令?或者使用 argparse 是不可能的? 对命令行变量进行分组还有其他建议吗?我查看了 Click,但对于我的任务,没有发现比标准 argparse 有任何优势。

注意:我是系统管理员,不是程序员,所以请对我进行非对象样式编码。 :)

谢谢

已解决 参数分组通过hpaulj 建议的答案解决。

import argparse
import pprint
parser = argparse.ArgumentParser()

group_list = ['group1', 'group2']

group1 = parser.add_argument_group('group1')
group1.add_argument('--test11', help="test11")
group1.add_argument('--test12', help="test12")

group2 = parser.add_argument_group('group2')
group2.add_argument('--test21', help="test21")
group2.add_argument('--test22', help="test22")

args = parser.parse_args()
pp = pprint.PrettyPrinter(indent=4)

d = dict()

for group in parser._action_groups:
    if group.title in group_list:
        d[group.title]=a.dest:getattr(args,a.dest,None) for a in group._group_actions

print "Parsed arguments"
pp.pprint(d)

这让我得到了问题 1 的预期结果。直到我将有多个具有相同名称的参数。解决方案可能看起来很难看,但至少可以按预期工作。

python argtest4.py --test22 aa  --test11 yy11 --test21 aaa21
Parsed arguments
   'group1':    'test11': 'yy11', 'test12': None,
    'group2':    'test21': 'aaa21', 'test22': 'aa'

【问题讨论】:

【参考方案1】:

您的问题太复杂,无法一口气理解和回答。但我会抛出一些初步的想法。

是的,argument_groups 只是在帮助中对参数进行分组的一种方式。它们对解析没有影响。

另一个最近的 SO 询问有关解析参数组的问题:

Is it possible to only parse one argument group's parameters with argparse?

该发帖人最初想使用一个组作为解析器,但 argparse 类结构不允许这样做。 argparse 以对象样式编写。 parser=ArguementParser... 创建一类对象,parser.add_arguement... 创建另一类,add_argument_group... 又一个。您可以通过继承 ArgumentParserHelpFormatterAction 类等来自定义它。

我提到了parents 机制。您定义一个或多个父解析器,并使用它们来填充您的“主”解析器。它们可以独立运行(使用 parse_known_args),而 'main' 用于处理帮助。

我们还讨论了在解析后对参数进行分组。 namespace 是一个简单的对象,其中每个参数都是一个属性。它也可以转换为字典。从字典中提取项目组很容易。

有关于使用多个子解析器的问题。这是一个尴尬的提议。可能,但并不容易。子解析器就像向系统程序发出命令。您通常在每次调用时发出一个命令。您不会嵌套它们或发出序列。您让 shell 管道和脚本处理多个操作。

IPython 使用argparse 解析其输入。它首先捕获帮助,然后发出自己的消息。大多数参数来自配置文件,因此可以使用默认配置、自定义配置和命令行设置值。这是命名大量参数的示例。

子解析器允许您使用相同的参数名称,但不能在一次调用中调用多个子解析器,这并没有多大帮助。即使您可以调用多个子解析器,它们仍然会将参数放在同一个命名空间中。 argparse 也尝试以独立于顺序的方式处理标记的参数。因此,命令行末尾的 --foo 会像在开头一样被解析。

在我们讨论使用参数名称('dest')的地方有一个问题,比如'group1.argument1',我什至讨论过使用嵌套命名空间。如果有帮助,我可以查一下。


另一个想法 - 加载 sys.argv 并在将其传递给一个或多个解析器之前对其进行分区。您可以将其拆分为某个关键字或前缀等。

【讨论】:

谢谢。链接为第一号挑战提供了有用的答案。现在我可以对选项进行分组并将它们移动到单独的字典中【参考方案2】:

如果您有这么多争论,这似乎是一个设计问题。这似乎非常难以管理。您不能使用具有合理默认设置的配置文件来实现这一点吗?或者代码中的默认值在命令行中使用合理(即小)数量的参数,并允许使用“键:值”配置文件中的参数覆盖所有内容或其他所有内容?我无法想象必须使用具有您提议的变量数量的 CLI。

【讨论】:

好吧,我已经有配置文件并且它们被成功使用了。这个想法是有能力从 CLI 覆盖这些值。很可能需要覆盖 10-15 个参数,但对这 10-15 个参数的选择可能来自整个可能的配置值集。仅供参考,完整的产品部署是 15-17 台主机,它们使用 Web 服务、2 个不同的数据库服务、2 个虚拟化后端,并以 5 种编程语言实现。可能是我错过了什么。 一个参数如何让您选择要使用的配置文件,然后将可覆盖的参数减少到最有可能进行更改的参数。 正如我所说,我已经有了配置文件的选项。实际上,其中有 3 个(现在是 2 个)- 具有默认值的文件和具有覆盖/环境特定变量的配置文件,具体取决于环境。然而,可以完成多个部署,链接到同一个环境,它们可以有几个或几十个不同的参数被覆盖。它是一个复杂的系统,有时过于复杂:D

以上是关于同时解析python中的多个子命令或以其他方式对解析的参数进行分组的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 python argparse 解析多个嵌套的子命令?

Python win32gui中的焦点子窗口

Ping 或以其他方式通过 C# 中的 MAC 判断设备是不是在网络上

如何减小 HtmlTableCell 的宽度或以其他方式将 HtmlTableRow 中的两个控件连接在一起?

带有通用子解析器命令的 Python argparse

将 SQL 地理转换为 SQL 几何或以其他方式提高查找速度