複雑なアプリケーション—ドキュメントをクリック
複雑なアプリケーション
Clickは、複雑で単純なCLIツールの作成を支援するように設計されています。 ただし、その設計の力は、システムを任意にネストできることです。 たとえば、Djangoを使用したことがある場合は、コマンドラインユーティリティが提供されていることに気付くでしょうが、Celeryも同様です。 CeleryをDjangoで使用する場合、相互に対話して相互構成する必要がある2つのツールがあります。
2つの別々のClickコマンドラインユーティリティの理論的な世界では、一方を他方の中にネストすることでこの問題を解決できます。 たとえば、Webフレームワークは、メッセージキューフレームワークのコマンドをロードすることもできます。
基本概念
これがどのように機能するかを理解するには、コンテキストと呼び出し規約の2つの概念を理解する必要があります。
コンテキスト
Clickコマンドが実行されるたびに、この特定の呼び出しの状態を保持する Context オブジェクトが作成されます。 解析されたパラメーター、それを作成したコマンド、関数の最後にクリーンアップする必要のあるリソースなどを記憶しています。 オプションで、アプリケーション定義のオブジェクトを保持することもできます。
コンテキストオブジェクトは、一番上のリストに到達するまでリンクリストを作成します。 各コンテキストは親コンテキストにリンクされています。 これにより、コマンドは別のコマンドの下で機能し、親コマンドの状態を変更することを恐れることなく、そこに独自の情報を格納できます。
ただし、親データが利用可能であるため、必要に応じて親データに移動することができます。
ほとんどの場合、コンテキストオブジェクトは表示されませんが、より複雑なアプリケーションを作成する場合に役立ちます。 これは私たちを次のポイントに導きます。
呼び出し規約
Clickコマンドのコールバックが実行されると、非表示になっていないすべてのパラメーターがキーワード引数として渡されます。 特に存在しないのはコンテキストです。 ただし、コールバックは、 pass_context()でマークすることにより、コンテキストオブジェクトに渡されることを選択できます。
では、コンテキストを受け取るべきかどうかわからない場合、どのようにコマンドコールバックを呼び出すのでしょうか。 答えは、コンテキスト自体が、これを実行できるヘルパー関数( Context.invoke())を提供するということです。 コールバックを最初の引数として受け入れ、関数を正しく呼び出します。
Gitクローンの構築
この例では、バージョン管理システムに似たコマンドラインツールを構築します。 Gitのようなシステムは通常、いくつかのパラメーターと構成をすでに受け入れている1つの包括的なコマンドを提供し、その後、他のことを行う追加のサブコマンドを備えています。
ルートコマンド
トップレベルでは、すべてのコマンドを保持できるグループが必要です。 この場合、基本的な click.group()を使用して、その下にある他のClickコマンドを登録できるようにします。
このコマンドでは、ツールの状態を構成するいくつかのパラメーターも受け入れます。
これが何をするのか理解しましょう。 サブコマンドを持つことができるグループコマンドを作成します。 呼び出されると、Repo
クラスのインスタンスが作成されます。 これは、コマンドラインツールの状態を保持します。 この場合、いくつかのパラメータを記憶しているだけですが、この時点で構成ファイルのロードなどを開始することもできます。
この状態オブジェクトは、コンテキストによって obj として記憶されます。 これは、コマンドが子に渡す必要があるものを記憶することになっている特別な属性です。
これを機能させるには、関数を pass_context()でマークする必要があります。そうしないと、コンテキストオブジェクトが完全に非表示になるためです。
最初の子コマンド
最初の子コマンドであるcloneコマンドを追加しましょう。
これでcloneコマンドができましたが、リポジトリにアクセスするにはどうすればよいですか? ご想像のとおり、1つの方法は、 pass_context()関数を使用することです。これにより、コールバックは、リポジトリを記憶したコンテキストも取得します。 ただし、 pass_obj()と呼ばれるこのデコレータの2番目のバージョンがあり、保存されたオブジェクト(この場合はリポジトリ)を渡すだけです。
インターリーブされたコマンド
構築したい特定のプログラムには関係ありませんが、インターリーブシステムに対する非常に優れたサポートもあります。 たとえば、バージョン管理システム用の非常に優れたプラグインがあり、多くの構成が必要であり、独自の構成を obj として保存したいとします。 その下に別のコマンドを添付すると、レポオブジェクトの代わりにプラグイン構成が突然取得されます。
これを修正する明白な方法の1つは、リポジトリへの参照をプラグインに保存することですが、コマンドは、そのようなプラグインの下に添付されていることを認識する必要があります。
コンテキストのリンクされた性質を利用することによって構築できるはるかに優れたシステムがあります。 プラグインコンテキストは、リポジトリを作成したコンテキストにリンクされていることがわかっています。 そのため、コンテキストによって格納されたオブジェクトがリポジトリであった最後のレベルの検索を開始できます。
これに対する組み込みのサポートは、 make_pass_decorator()ファクトリによって提供されます。このファクトリは、オブジェクトを検索するデコレータを作成します( Context.find_object()を内部的に呼び出します)。 この場合、最も近いRepo
オブジェクトを見つけたいことがわかっているので、このためのデコレータを作成しましょう。
pass_obj
の代わりにpass_repo
を使用すると、他の何かの代わりに常にリポジトリが取得されます。
オブジェクト作成の保証
上記の例は、Repo
オブジェクトを作成してコンテキストに保存する外部コマンドがある場合にのみ機能します。 より高度なユースケースでは、これが問題になる可能性があります。 make_pass_decorator()のデフォルトの動作は、オブジェクトを検索する Context.find_object()を呼び出すことです。 オブジェクトが見つからない場合、 make_pass_decorator()はエラーを発生させます。 別の動作は、 Context.ensure_object()を使用してオブジェクトを検索し、オブジェクトが見つからない場合はオブジェクトを作成して、最も内側のコンテキストに格納することです。 この動作は、ensure=True
を渡すことにより、 make_pass_decorator()に対しても有効にできます。
この場合、最も内側のコンテキストは、オブジェクトが欠落している場合に作成されたオブジェクトを取得します。 これにより、以前に配置されていたオブジェクトが置き換えられる可能性があります。 この場合、外部コマンドが実行されなくても、コマンドは実行可能のままです。 これが機能するためには、オブジェクトタイプに引数を受け入れないコンストラクターが必要です。
そのため、スタンドアロンで実行されます。
ご覧のように: