コマンドとグループ—ドキュメントをクリックします
コマンドとグループ
Clickの最も重要な機能は、コマンドラインユーティリティを任意にネストするという概念です。 これは、コマンドおよびグループ(実際にはマルチコマンド)を介して実装されます。
コールバックの呼び出し
通常のコマンドの場合、コマンドが実行されるたびにコールバックが実行されます。 スクリプトが唯一のコマンドである場合、スクリプトは常に起動します(パラメーターコールバックがそれを妨げない限り)。 これは、たとえば、誰かが--help
をスクリプトに渡した場合に発生します)。
グループとマルチコマンドの場合、状況は異なります。 この場合、(この動作が変更されない限り)サブコマンドが起動するたびにコールバックが起動します。 これが実際に意味することは、内部コマンドが実行されるときに外部コマンドが実行されるということです。
これは次のようになります。
パラメータの受け渡し
クリックは、コマンドとサブコマンドの間でパラメーターを厳密に分離します。 つまり、特定のコマンドのオプションと引数は、コマンド名自体の後で指定する必要がありますが、他のコマンド名の前で指定する必要があります。
この動作は、事前定義された--help
オプションですでに観察できます。 sub
というサブコマンドを含むtool.py
というプログラムがあるとします。
tool.py --help
は、プログラム全体のヘルプを返します(サブコマンドのリスト)。tool.py sub --help
は、sub
サブコマンドのヘルプを返します。- ただし、
tool.py --help sub
は--help
をメインプログラムの引数として扱います。 次に、Clickは、--help
のコールバックを呼び出します。これにより、ヘルプが出力され、Clickがサブコマンドを処理する前にプログラムが中止されます。
ネストされた処理とコンテキスト
前の例からわかるように、基本コマンドグループは、コールバックに渡されるデバッグ引数を受け入れますが、同期コマンド自体には渡されません。 syncコマンドは、独自の引数のみを受け入れます。
これにより、ツールは互いに完全に独立して動作できますが、1つのコマンドがネストされたコマンドとどのように通信するのでしょうか。 これに対する答えはコンテキストです。
コマンドが呼び出されるたびに、新しいコンテキストが作成され、親コンテキストにリンクされます。 通常、これらのコンテキストは表示されませんが、表示されます。 コンテキストは、値とともにパラメータコールバックに自動的に渡されます。 コマンドは、 pass_context()デコレータで自分自身をマークすることにより、渡されるコンテキストを要求することもできます。 その場合、コンテキストは最初の引数として渡されます。
コンテキストは、プログラムの目的に使用できるプログラム指定のオブジェクトを運ぶこともできます。 これが意味するのは、次のようなスクリプトを作成できるということです。
オブジェクトが提供されている場合、各コンテキストはオブジェクトをその子に渡しますが、どのレベルでもコンテキストのオブジェクトをオーバーライドできます。 親に連絡するには、context.parent
を使用できます。
それに加えて、オブジェクトを渡す代わりに、アプリケーションがグローバル状態を変更することを妨げるものは何もありません。 たとえば、グローバルDEBUG
変数を反転するだけで、それを実行できます。
装飾コマンド
前の例で見たように、デコレータはコマンドの呼び出し方法を変更できます。 実際に舞台裏で行われているのは、コールバックは常に Context.invoke()メソッドを介して呼び出されることです。このメソッドは、コマンドを自動的に正しく呼び出します(コンテキストを渡すかどうかに関係なく)。
これは、カスタムデコレータを作成する場合に非常に便利です。 たとえば、一般的なパターンは、状態を表すオブジェクトを構成してコンテキストに格納し、カスタムデコレータを使用してこの種の最新のオブジェクトを検索し、それを最初の引数として渡すことです。
たとえば、 pass_obj()デコレータは次のように実装できます。
Context.invoke()コマンドは自動的に正しい方法で関数を呼び出すため、関数はf(ctx, obj)
またはf(obj)
のいずれかで呼び出されます。それ自体は pass_context()で装飾されています。
これは非常に強力な概念であり、非常に複雑なネストされたアプリケーションを構築するために使用できます。 詳細については、複雑なアプリケーションを参照してください。
コマンドなしのグループ呼び出し
デフォルトでは、サブコマンドが渡されない限り、グループまたはマルチコマンドは呼び出されません。 実際、コマンドを指定しないと、デフォルトで--help
が自動的に渡されます。 この動作は、invoke_without_command=True
をグループに渡すことで変更できます。 その場合、ヘルプページを表示する代わりに、コールバックが常に呼び出されます。 コンテキストオブジェクトには、呼び出しがサブコマンドに送られるかどうかに関する情報も含まれています。
例:
そしてそれが実際にどのように機能するか:
カスタムマルチコマンド
click.group()を使用することに加えて、独自のカスタムマルチコマンドを作成することもできます。 これは、プラグインから遅延して読み込まれるコマンドをサポートする場合に役立ちます。
カスタムマルチコマンドは、リストとロードメソッドを実装する必要があります。
これらのカスタムクラスは、デコレータでも使用できます。
マルチコマンドのマージ
カスタムマルチコマンドの実装に加えて、複数を1つのスクリプトにマージすることも興味深い場合があります。 これは一般に、上下にネストするほど推奨されませんが、マージアプローチは、状況によってはシェルエクスペリエンスを向上させるのに役立ちます。
このようなマージシステムのデフォルトの実装は、 CommandCollection クラスです。 他のマルチコマンドのリストを受け入れ、コマンドを同じレベルで使用できるようにします。
使用例:
そしてそれはどのように見えるか:
コマンドが複数のソースに存在する場合、最初のソースが優先されます。
マルチコマンドチェーン
バージョン3.0の新機能。
一度に複数のサブコマンドを呼び出せるようにすると便利な場合があります。 たとえば、以前にsetuptoolsパッケージをインストールした場合、[X146X] の前にbdist_wheel
の前にdist
を呼び出すsetup.py sdist bdist_wheel upload
コマンドチェーンに精通している可能性があります。 Click 3.0以降、これは非常に簡単に実装できます。 あなたがしなければならないのはあなたのマルチコマンドにchain=True
を渡すことです:
これで、次のように呼び出すことができます。
マルチコマンドチェーンを使用する場合、引数にnargs=-1
を使用できるコマンドは1つ(最後)のみです。 チェーンされたマルチコマンドの下にマルチコマンドをネストすることもできません。 それ以外は、それらがどのように機能するかについての制限はありません。 彼らは通常通りオプションと引数を受け入れることができます。 オプションと引数の間の順序は、連鎖コマンドでは制限されています。 現在、--options argument
の注文のみが許可されています。
別の注意: Context.invoked_subcommand 属性は、複数のコマンドが呼び出された場合に値として'*'
を与えるため、マルチコマンドには少し役に立ちません。 これが必要なのは、サブコマンドの処理が次々に行われるため、コールバックの起動時に処理される正確なサブコマンドがまだ利用できないためです。
ノート
現在、チェーンコマンドをネストすることはできません。 これは、Clickの将来のバージョンで修正される予定です。
マルチコマンドパイプライン
バージョン3.0の新機能。
マルチコマンドチェーンの非常に一般的な使用例は、1つのコマンドに前のコマンドの結果を処理させることです。 これを容易にする方法はいくつかあります。 最も明白な方法は、コンテキストオブジェクトに値を格納し、それを関数から関数へと処理することです。 これは、関数を pass_context()で装飾することで機能します。その後、コンテキストオブジェクトが提供され、サブコマンドはそのデータをそこに格納できます。
これを実現する別の方法は、処理関数を返すことによってパイプラインをセットアップすることです。 次のように考えてください。サブコマンドが呼び出されると、サブコマンドはすべてのパラメーターを処理し、その処理方法の計画を立てます。 その時点で、処理関数を返し、戻ります。
返された関数はどこに行きますか? 連鎖マルチコマンドは、MultiCommand.resultcallback()
にコールバックを登録して、これらすべての関数を調べてから呼び出すことができます。
これをもう少し具体的にするために、この例を考えてみましょう。
それは一度にたくさんあるので、それを段階的に見ていきましょう。
- まず、チェーン可能な group()を作成します。 それに加えて、サブコマンドが定義されていない場合でも、Clickに呼び出すように指示します。 これが行われない場合、空のパイプラインを呼び出すと、結果のコールバックを実行する代わりにヘルプページが生成されます。
- 次に行うことは、グループに結果コールバックを登録することです。 このコールバックは、すべてのサブコマンドのすべての戻り値のリストである引数と、グループ自体と同じキーワードパラメーターを使用して呼び出されます。 これは、コンテキストオブジェクトを使用せずに、そこで入力ファイルに簡単にアクセスできることを意味します。
- この結果のコールバックでは、入力ファイル内のすべての行のイテレーターを作成し、このイテレーターをすべてのサブコマンドから返されたすべてのコールバックに渡し、最後にすべての行をstdoutに出力します。
その後、必要な数のサブコマンドを登録でき、各サブコマンドはプロセッサ関数を返して行のストリームを変更できます。
注意すべき重要な点の1つは、各コールバックが実行された後、Clickがコンテキストをシャットダウンすることです。 これは、たとえば、プロセッサ関数ではファイルがすでに閉じられているため、ファイルタイプにアクセスできないことを意味します。 この制限は、リソースの処理をはるかに複雑にするため、変更される可能性はほとんどありません。 そのため、ファイルタイプを使用せず、 open_file()を使用して手動でファイルを開くことをお勧めします。
パイプラインの処理を改善するより複雑な例については、Clickリポジトリの imagepipeマルチコマンドチェーンデモをご覧ください。 これは、パイプラインの優れた内部構造を持つパイプラインベースの画像編集ツールを実装しています。
デフォルトのオーバーライド
デフォルトでは、パラメータのデフォルト値は、定義時に提供されるdefault
フラグから取得されますが、デフォルトをロードできるのはそれだけではありません。 もう1つの場所は、コンテキスト上のContext.default_map
(辞書)です。 これにより、デフォルトを構成ファイルからロードして、通常のデフォルトをオーバーライドできます。
これは、別のパッケージからいくつかのコマンドをプラグインしたが、デフォルトに満足できない場合に役立ちます。
デフォルトのマップは、サブコマンドごとに任意にネストできます。
default_map = {
"debug": True, # default for a top level option
"runserver": {"port": 5000} # default for a subcommand
}
デフォルトのマップは、スクリプトが呼び出されたときに提供することも、コマンドによって任意の時点でオーバーライドすることもできます。 たとえば、最上位のコマンドは、構成ファイルからデフォルトをロードできます。
使用例:
そして実際に:
コンテキストのデフォルト
バージョン2.0の新機能。
Click 2.0以降では、スクリプトを呼び出すときだけでなく、コマンドを宣言するデコレータでも、コンテキストのデフォルトを上書きできます。 たとえば、カスタムdefault_map
を定義する前の例を考えると、これはデコレータでも実行できるようになりました。
この例は、前の例と同じです。
そして再び実際の例:
コマンドの戻り値
バージョン3.0の新機能。
Click 3.0の新しい導入の1つは、コマンドコールバックからの戻り値の完全なサポートです。 これにより、以前は実装が困難であったすべての機能が可能になります。
本質的に、コマンドコールバックは値を返すことができるようになりました。 この戻り値は、特定のレシーバーにバブルされます。 このユースケースの1つは、マルチコマンドチェーンの例ですでに示されています。ここでは、チェーンされたマルチコマンドがすべての戻り値を処理するコールバックを持つことができることが示されています。
Clickでコマンドの戻り値を操作する場合、次のことを知っておく必要があります。
- コマンドコールバックの戻り値は、通常、 BaseCommand.invoke()メソッドから返されます。 このルールの例外は、グループと関係があります。
- グループでは、戻り値は通常、呼び出されたサブコマンドの戻り値です。 このルールの唯一の例外は、引数なしで呼び出され、 invoke_without_command が有効になっている場合、戻り値がグループコールバックの戻り値であることです。
- グループがチェーン用に設定されている場合、戻り値はすべてのサブコマンドの結果のリストです。
- グループの戻り値は、 MultiCommand.result_callback を介して処理できます。 これは、チェーンモードのすべての戻り値のリスト、またはチェーンされていないコマンドの場合は単一の戻り値で呼び出されます。
- 戻り値は、 Context.invoke()および Context.forward()メソッドからバブルスルーされます。 これは、内部で別のコマンドを呼び出したい場合に役立ちます。
- Clickには戻り値に関する厳しい要件はなく、それ自体は使用されません。 これにより、戻り値をカスタムデコレータまたはワークフローに使用できます(マルチコマンドチェーンの例のように)。
- Clickスクリプトがコマンドラインアプリケーションとして( BaseCommand.main()を介して)呼び出されると、 Standalone_mode が無効になっていない限り、戻り値は無視されます。