2011-12-14 19 views
5

Pythonでcmdlnを使用して "ネストされた"サブコマンドを実装しています。Pythonで "ネスト"サブコマンドをどのように実装すればよいですか?

ここで正しい用語を使用しているかどうかはわかりません。私はcmdlnを使ってコマンドラインツールを実装しようとしています。これは "入れ子になった"サブコマンドを可能にします。現実世界の例を次に示します。

git svn rebase 

これを実装する最良の方法は何ですか。私はdoc、ここ、そしてウェブ全体でこれに関する詳細情報を探していますが、空になっています。

これは自動的にこれを行う文書化されていない機能ではありません。前のサブコマンドハンドラでは、別のサブコマンドがあると判断し、コマンドディスパッチャを再度ディスパッチさせることを考えました。私はcmdlnの内部を調べましたが、ディスパッチャはプライベートメソッド_dispatch_cmdです。私の次の考えは、私自身のサブコマンド・ディスパッチャーを作成することですが、それは理想的でなく、乱雑です。

ご協力いただければ幸いです。

答えて

5

argparseは、サブコマンドを非常に簡単にします。

+1

私が働いている会社はそうargparseを使用してV2.6ベースラインは、それが外部ライブラリとして含まれており、必要な場合にのみロードされなければならないという点で問題がある持っています。不可能から遠い、理想的ではない。 cmdlnライブラリに関しては、私が再作成したくないベースライン機能の素敵なビットを私に与えます。それは私が何か他のものを使うことに反対していると言った。 – tima

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'])

出力:それは二-yオプションを消費しないことを

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

注意。 結果 'args'を別のArgumentParserに渡すことができます。

欠点:

  • ヘルプがうまく処理されない場合があります。
  • エラーが発生するのは追跡が難しく、エラーメッセージが適切に連鎖されていることを確認するために何らかの努力が必要な場合があります。
  • 複数のArgumentParserに関連付けられたオーバーヘッドが少し増加します。

これ以上入力がない場合は、教えてください。

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() 
関連する問題