2011-12-14 99 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()