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

tim*_*ima 8 python command-line subcommand

使用cmdln在Python中实现"嵌套"子命令.

我不确定我在这里使用正确的术语.我正在尝试使用cmdln实现一个命令行工具,它允许"嵌套"子命令.这是一个真实世界的例子:

git svn rebase
Run Code Online (Sandbox Code Playgroud)

实现这个的最佳方法是什么?我一直在doc,这里和整个网络上搜索关于这方面的更多信息,但是空洞了.(也许我正在用错误的术语进行搜索.)

如果没有自动执行此操作的未记录功能,我最初的想法是让前一个子命令处理程序确定存在另一个子命令并再次调度命令调度程序.我查看了cmdln的内部结构,调度程序是一个私有方法,_dispatch_cmd.我的下一个想法是创建我自己的子子命令调度程序,但这似乎不太理想和混乱.

任何帮助,将不胜感激.

Chr*_*sse 6

argparse使子命令非常容易.


小智 6

迟到了这里的聚会,但我不得不做了相当多的事情,发现argparse这很蠢.这促使我编写了一个argparse名为arghandler的扩展,它对此有明确的支持 - 可以实现基本上零行代码的子命令.

这是一个例子:

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()
Run Code Online (Sandbox Code Playgroud)


eac*_*eau 5

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

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

Usage: tool [-y] {a, b}
  a [-x] {create, delete}
    create [-x]
    delete [-y]
  b [-y] {push, pull}
    push [-x]
    pull [-x]
Run Code Online (Sandbox Code Playgroud)
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'])
Run Code Online (Sandbox Code Playgroud)

输出

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)
Run Code Online (Sandbox Code Playgroud)

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

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

我觉得一个稍微便宜但有用的解决方法是让 argparse 将以下参数解释为列表中的字符串。这可以通过将前缀设置为空终止符“\0”(或其他一些“难以使用”的字符)来完成 - 如果前缀为空,代码将抛出错误,至少在 Python 2.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'])
Run Code Online (Sandbox Code Playgroud)

输出:

Namespace(args=['-y', '12'], command='a', x=True, y=True)
Run Code Online (Sandbox Code Playgroud)

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

缺点:

  • 帮助可能处理不好。必须为此做一些更多的解决方法
  • 遇到错误可能很难追踪,并且需要一些额外的努力来确保错误消息正确链接。
  • 与多个 ArgumentParsers 相关的开销会多一些。

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