2011-12-14 98 views
5

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

我不确定我在这里使用了正确的术语。我试图使用cmdln来实现一个允许“嵌套”子命令的命令行工具。这是一个真实世界的例子:

git svn rebase 

实现这个的最佳方式是什么?我一直在寻找有关这方面的更多信息,在这里和整个网络上,但已经空了。 (也许我是在用错误的术语搜索)。

缺少一个自动执行此操作的未记录功能,我最初的想法是让以前的子命令处理程序确定是否有另一个子命令并再次分派命令分派器。我已经查看了cmdln的内部信息,调度程序是一个私有方法_dispatch_cmd。我的下一个想法是创建我自己的子命令调度程序,但这似乎不太理想和混乱。

任何帮助,将不胜感激。

回答

5

argparse使得子命令非常简单。

+1

公司我工作的有那么使用argparse一个V2.6基线是,它必须被纳入作为外部库,仅在需要时加载的问题。远非不可能,只是不理想。至于cmdln库,给我一些我不希望重新创建的基本功能。这就是说我反对使用别的东西。 – tima 2011-12-16 19:27:30

4

我觉得argparse中的sub_parsers有一个小小的限制,如果说,你有一套工具可能有类似的选项,可能在不同层次上传播。出现这种情况可能很少,但如果您正在编写可插拔/模块化代码,则可能会发生。

我有下面的例子。它是牵强和目前没有得到很好的解释,因为它是比较晚的,但这里有云:

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'(或其他'难以使用'的字符)来完成 - 如果前缀为空,那么代码会抛出一个错误,至少在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'])

输出:

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

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

缺点:

  • 帮助可能不能很好地处理。将不得不采取更多的解决方法
  • 遇到错误可能很难追踪,并需要一些额外的努力,以确保错误消息是正确链接。
  • 与多个ArgumentParsers相关的一点额外开销。

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

5

这次派对的晚会,但我不得不这样做了很多,并发现​​相当笨重的做到这一点。这促使我写一个​​的扩展名为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()