複雑なアプリケーション

Clickは、複雑で簡単なCLIツールの作成を支援することを目的としています。しかし,その設計の力は,システムを任意に入れ子にすることができることである.例えば、もしあなたがDjangoを使用したことがあれば、あなたはそれがコマンドライン実用プログラムを提供していることを認識するだろうが、セロリもそうだ。Djangoでセロリを用いた場合,2つのツールが相互に交互に交差配置する必要がある.

2つの単独のClickコマンドラインユーティリティからなる論理世界では,1つを他方に入れ子にすることでこの問題を解決することができる.たとえば,Webフレームワークはメッセージキューフレームワークのコマンドをロードすることも可能である.

基本概念.

これがどのように動作しているのかを理解するためには、文脈と呼び出し約束の2つの概念を理解する必要があります。

文脈.

Clickコマンドが実行されるたびに Context この特定の呼び出し状態を保存するオブジェクトを作成します。これは,解析されたパラメータ,それを作成する命令,関数終了時にどのような資源を掃除する必要があるかなどを記憶する.これはまた,アプリケーションが定義したオブジェクトを選択的に保存することができる.

文脈オブジェクトは,それらがトップに達するまでリンクテーブルを構築する.各コンテキストは親コンテキストにリンクされる.これにより、1つのコマンドが別のコマンドの下で動作することを可能にし、親コマンドの状態を変更する心配なく、自分の情報をそこに格納することができる。

しかし,親データは利用可能であるため,必要であればナビゲーションすることができる.

Contextオブジェクトはほとんど見られませんが、より複雑なアプリケーションを作成する際に役立ちます。これで私たちを次の点に連れて行きました。

呼び出し約束

Clickコマンドコールバックが実行されると,すべての非隠れパラメータをキーワードパラメータとして渡す.注目すべきは、文脈がないということだ。しかし,コールバックは文脈対象に渡すことを選択することができ,それ自体をタグ付けする方法である pass_context() それがそうです。

では,コンテキストを受信すべきかどうか分からなければ,どのように命令コールを呼び出すのであろうか.答えは文脈自体がブースター関数を提供しているということです (Context.invoke() )これをすることができますこれはコールバックを第1のパラメータとして受け取り,関数を正しく呼び出す.

Gitクローンの構築

本例では、バージョン制御システムと同様のコマンドラインツールを構築することが望ましい。Gitのようなシステムは、一般に、いくつかのパラメータおよび構成を受け入れた包括的なコマンドを提供し、その後、他のことをするための追加のサブコマンドを提供する。

ルート命令だ

トップレベルで、私たちはすべての命令を収容できるグループが必要だ。この例では基本的な click.group() これは私たちがその下に他のクリックコマンドを登録することを可能にする。

このコマンドについては、ツールの状態を構成するパラメータも受け入れたいと思います。

import os
import click


class Repo(object):
    def __init__(self, home=None, debug=False):
        self.home = os.path.abspath(home or '.')
        self.debug = debug


@click.group()
@click.option('--repo-home', envvar='REPO_HOME', default='.repo')
@click.option('--debug/--no-debug', default=False,
              envvar='REPO_DEBUG')
@click.pass_context
def cli(ctx, repo_home, debug):
    ctx.obj = Repo(repo_home, debug)

これが何をしているのか調べてみましょう。サブコマンドを含むグループコマンドを作成します。これを呼び出すと作成されます Repo 級友たち。これは私たちの命令行ツールの状態を保存している。本例では、いくつかのパラメータのみを記憶するが、この点では、プロファイルのロードを開始し、以下同様であることもできる。

そして,文脈はその状態オブジェクトを obj それがそうです。これは特別な属性であり、コマンドはそれらがそのサブレベルに渡されなければならない内容を記憶しなければならない。

正常に動作させるためには関数をマークする必要があります pass_context() そうでなければ、文脈オブジェクトは私たちに完全に隠されるからだ。

第一子命令

最初のサブコマンドやクローン命令を追加しましょう

@cli.command()
@click.argument('src')
@click.argument('dest', required=False)
def clone(src, dest):
    pass

では今私たちはクローン命令を持っていますがどうやって買い戻しにアクセスするのでしょうか?ご想像のように1つの方法は pass_context() 関数,この関数は,再び我々のコールバックも買い戻し時に伝達される文脈を記憶させる.しかしこの装飾器には別のバージョンがあります pass_obj() これは格納されたオブジェクトのみを渡します(私たちの例ではrepo):

@cli.command()
@click.argument('src')
@click.argument('dest', required=False)
@click.pass_obj
def clone(repo, src, dest):
    pass

互い違い命令

我々が構築する特定のプログラムとは無関係であるが,インターリーブシステムをかなり支援している.例えば、私たちのバージョン制御システムは、大量の構成を行う必要があり、自分の構成を格納したい非常にクールなプラグインを持っていると仮定します。 obj それがそうです。もし私たちがその後次に別のコマンドを追加したら、私たちは突然プラグイン構成を得て、私たちのrepoオブジェクトではありません。

明らかな修復方法は、repoへの参照をプラグインに格納することであるが、コマンドは、そのようなプラグインの下に追加されていることを知る必要がある。

文脈のリンクの性質を利用して構築できるより良いシステムがある.私たちはプラグインの文脈が買い戻しを作成する文脈を受けていることを知っている。したがって,コンテキストメモリのオブジェクトが買い戻しの最後のレベルであるという検索を開始することができる.

これに対する内蔵サポートは make_pass_decorator() Factoryは、オブジェクトを探す装飾器を作成してくれます(内部で呼び出されています Context.find_object() )。私たちの例では最も近いものを見つけたいと思っています Repo 相手に装飾器を作ってみましょう

pass_repo = click.make_pass_decorator(Repo)

もし私たちが今使っているなら pass_repo ではなく pass_obj 私たちはいつも買い戻しを受けます他のものではありません

@cli.command()
@click.argument('src')
@click.argument('dest', required=False)
@pass_repo
def clone(repo, src, dest):
    pass

作成対象の確保

上記の例では、外部コマンドのみが作成されます Repo オブジェクトはコンテキストに格納される.いくつかのより高度な用例については、これが問題になるかもしれない。デフォルトの行動をしています make_pass_decorator() 電話することです Context.find_object() この物体を見つけますもし物体が見つからなければ make_pass_decorator() 間違いを引き起こすことになりますもう一つの行為は Context.ensure_object() それは、オブジェクトが見つかり、見つからない場合、オブジェクトを作成し、最も奥のコンテキストに格納する。この行為を以下のオブジェクトに有効にすることも可能である make_pass_decorator() 通りすがりに ensure=True

pass_repo = click.make_pass_decorator(Repo, ensure=True)

この場合、オブジェクトが欠落している場合、最奥のコンテキストは、作成されたオブジェクトを取得する。これは以前そこに置かれていたオブジェクトを置き換えるかもしれない。この場合、外部命令が実行されなくても、その命令は実行可能状態を維持する。これを実現するためには,オブジェクトタイプには何のパラメータも受け入れない構造関数が必要である.

独立して動作することができます

@click.command()
@pass_repo
def cp(repo):
    click.echo(isinstance(repo, Repo))

ご覧のように

$ cp
True