我应该如何在Python中实现“嵌套”子命令?

2024-06-07 22:01:08 发布

您现在位置:Python中文网/ 问答频道 /正文

使用cmdln在Python中实现“嵌套”子命令。

我不确定我用的术语是否正确。我正在尝试使用cmdln实现一个命令行工具,该工具允许“嵌套”子命令。下面是一个真实的例子:

git svn rebase

实现这一目标的最佳方法是什么?我一直在寻找更多关于这个的信息,在这里和整个网络上,但都是空的。(也许我用的是错误的词。)

除了一个自动执行此操作的未记录功能外,我最初的想法是让前面的子命令处理程序确定是否存在另一个子命令,然后再次分派命令调度程序。不过,我已经研究了cmdln的内部结构,dispatcher是一个私有方法。我的下一个想法是创建自己的子命令调度器,但这似乎不太理想和混乱。

任何帮助都将不胜感激。


Tags: 工具方法命令行git命令网络信息目标
3条回答

argparse使子命令非常简单。

参加派对很晚了,但我不得不这么做,我发现用它来做很笨拙。这促使我编写了一个名为arghandlerargparse的扩展,它明确支持使用基本上零行代码实现子命令。

下面是一个例子:

from arghandler import *

@subcmd
def push(context,args):
    print 'command: push'

@subcmd
def pull(context,args):
    print 'command: pull'

# run the command - which will gather up all the subcommands
handler = ArgumentHandler()
handler.run()

我觉得argparse中的子解析器有一个小小的限制,如果说,您有一套工具,它们可能有类似的选项,可能分布在不同的级别上。这种情况可能很少见,但如果您正在编写可插入/模块化代码,则可能会发生这种情况。

我有下面的例子。这是牵强的,目前还没有很好的解释,因为已经很晚了,但在这里:

Usage: tool [-y] {a, b}
  a [-x] {create, delete}
    create [-x]
    delete [-y]
  b [-y] {push, pull}
    push [-x]
    pull [-x]
from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument('-x', action = 'store_true')
parser.add_argument('-y', action = 'store_true')

subparsers = parser.add_subparsers(dest = 'command')

parser_a = subparsers.add_parser('a')
parser_a.add_argument('-x', action = 'store_true')
subparsers_a = parser_a.add_subparsers(dest = 'sub_command')
parser_a_create = subparsers_a.add_parser('create')
parser_a_create.add_argument('-x', action = 'store_true')
parser_a_delete = subparsers_a.add_parser('delete')
parser_a_delete.add_argument('-y', action = 'store_true')

parser_b = subparsers.add_parser('b')
parser_b.add_argument('-y', action = 'store_true')
subparsers_b = parser_b.add_subparsers(dest = 'sub_command')
parser_b_create = subparsers_b.add_parser('push')
parser_b_create.add_argument('-x', action = 'store_true')
parser_b_delete = subparsers_b.add_parser('pull')
parser_b_delete.add_argument('-y', action = 'store_true')

print parser.parse_args(['-x', 'a', 'create'])
print parser.parse_args(['a', 'create', '-x'])
print parser.parse_args(['b', '-y', 'pull', '-y'])
print parser.parse_args(['-x', 'b', '-y', 'push', '-x'])

输出

Namespace(command='a', sub_command='create', x=True, y=False)
Namespace(command='a', sub_command='create', x=True, y=False)
Namespace(command='b', sub_command='pull', x=False, y=True)
Namespace(command='b', sub_command='push', x=True, y=True)

如您所见,很难区分每个参数设置在链上的位置。 可以通过更改每个变量的名称来解决此问题。例如,您可以将“dest”设置为“x”、“a_x”、“a_create_x”、“b_push_x”等,但这会很痛苦,很难分离。

另一种方法是在ArgumentParser到达子命令时停止它,并将剩余的参数传递给另一个独立的解析器,以便它可以生成单独的对象。 您可以尝试通过使用“parse_known_args()”而不是为每个子命令定义参数来实现这一点。但是,这并不好,因为之前的任何未解析参数仍然存在,可能会混淆程序。

我觉得有点便宜,但有用的解决方法是让argparse将以下参数解释为列表中的字符串。这可以通过将前缀设置为空终止符“\0”(或其他一些“难以使用”字符)来完成-如果前缀为空,代码将抛出一个错误,至少在Python2.7.3中是这样。

示例:

parser = ArgumentParser()
parser.add_argument('-x', action = 'store_true')
parser.add_argument('-y', action = 'store_true')
subparsers = parser.add_subparsers(dest = 'command')
parser_a = subparsers.add_parser('a' prefix_chars = '\0')
parser_a.add_argument('args', type = str, nargs = '*')

print parser.parse_args(['-xy', 'a', '-y', '12'])

输出:

Namespace(args=['-y', '12'], command='a', x=True, y=True)

注意,它不使用第二个-y选项。 然后可以将结果“args”传递给另一个ArgumentParser。

缺点:

  • 帮助可能处理得不好。我得再做些变通
  • 遇到错误可能很难跟踪,需要额外的努力来确保错误消息被正确链接。
  • 与多个参数分析器相关联的开销稍微多一些。

如果有人对此有更多的意见,请告诉我。

相关问题 更多 >

    热门问题