高度モード

ライブラリ自体で実現される一般的な機能に加えて,Clickを拡張することで無数のモデルを実現することができる.このページは私たちが達成できる目標にいくつかの洞察力を持たせなければならない。

命令別名

多くのツールサポートコマンドの別名(参照) Command alias example )。例えば、構成することができます git 受け入れ、受け入れる git ci の別名として git commit それがそうです。他のツールは,エイリアスを自動的に短縮することでエイリアスの自動発見を支援している.

Clickはこのボックスを開けるための機能をサポートしていないが,容易にカスタマイズできる. Group 他にも MultiCommand この機能を提供しています

中で説明したのは カスタムマルチコマンド マルチコマンドは、2つの方法を提供することができる。 list_commands() そして get_command() それがそうです。この場合、あなたは一般に混同を避けるために、ヘルプページ上の別名を列挙したくないので、後者をカバーするだけでいいです。

以下の例の実装 Group これは命令の接頭辞を受け取る.もし命令があれば push それは受け入れられます pus 別名として(それが唯一であれば):

class AliasedGroup(click.Group):

    def get_command(self, ctx, cmd_name):
        rv = click.Group.get_command(self, ctx, cmd_name)
        if rv is not None:
            return rv
        matches = [x for x in self.list_commands(ctx)
                   if x.startswith(cmd_name)]
        if not matches:
            return None
        elif len(matches) == 1:
            return click.Group.get_command(self, ctx, matches[0])
        ctx.fail(f"Too many matches: {', '.join(sorted(matches))}")

このように使うことができます

@click.command(cls=AliasedGroup)
def cli():
    pass

@cli.command()
def push():
    pass

@cli.command()
def pop():
    pass

パラメータ修正

ご覧のように、パラメータ(オプションおよび引数)はコマンドコールバックに転送されます。パラメータをコールバックに渡すのを防ぐ一般的な方法は expose_value パラメータはそのパラメータを完全に隠蔽するパラメータに変換する.その働き方は Context 対象は一つ持っている params 属性,その属性はすべてのパラメータの辞書である.辞書中の内容が何であっても,コールバック関数に渡される.

これは、追加パラメータを補完するために使用することができる。一般に、このモデルの使用は推奨されていないが、場合によっては有用である可能性がある。少なくともシステムがこのような方法で働くのは良いことだということを知っている。

import urllib

def open_url(ctx, param, value):
    if value is not None:
        ctx.params['fp'] = urllib.urlopen(value)
        return value

@click.command()
@click.option('--url', callback=open_url)
def cli(url, fp=None):
    if fp is not None:
        click.echo(f"{url}: {fp.code}")

この場合、コールバック戻りURLは変更されないが、2番目の転送も行われる。 fp 値はコールバックに設定した.しかし、より推奨されているのは、パッケージで情報を伝達することです。

import urllib

class URL(object):

    def __init__(self, url, fp):
        self.url = url
        self.fp = fp

def open_url(ctx, param, value):
    if value is not None:
        return URL(value, urllib.urlopen(value))

@click.command()
@click.option('--url', callback=open_url)
def cli(url):
    if url is not None:
        click.echo(f"{url.url}: {url.fp.code}")

トークンを正規化する.

Changelog

バージョン 2.0 で追加.

Click 2.0から,正規化トークンのための関数を提供することができる.トークンは、オプション名、選択値、またはコマンド値とすることができる。例えば、これは、大文字と小文字を区別しないオプションを実装するために使用されてもよい。

この機能を使用するためには、トークンの正規化を実行する関数をコンテキストに渡す必要がある。例えば、1つの関数を使用してトークンを小文字に変換することができます。

CONTEXT_SETTINGS = dict(token_normalize_func=lambda x: x.lower())

@click.command(context_settings=CONTEXT_SETTINGS)
@click.option('--name', default='Pete')
def cli(name):
    click.echo(f"Name: {name}")

コマンドラインでの動作方法です

$ cli --NAME=Pete
Name: Pete

他の命令を呼び出す

時々、1つのコマンドから別のコマンドを呼び出すのが面白いかもしれません。これはClickが一般的に使用を奨励しないモデルだが、依然として可能だ。そのため、ご利用いただけます Context.invoke() あるいは…。 Context.forward() 方法です。

彼らの動作原理は似ていますが違いは Context.invoke() 呼び出し元として提供されるパラメータのみを使用して別のコマンドを呼び出します。 Context.forward() 現在のコマンドのパラメータを充填します。両方とも最初のパラメータとしてコマンドを受け取り、他のすべてのコンテンツはあなたが予想したように転送し続けます。

例:

cli = click.Group()

@cli.command()
@click.option('--count', default=1)
def test(count):
    click.echo(f'Count: {count}')

@cli.command()
@click.option('--count', default=1)
@click.pass_context
def dist(ctx, count):
    ctx.forward(test)
    ctx.invoke(test, count=42)

どのように見えるのでしょうか

$ cli dist
Count: 1
Count: 42

評価順序をコールバックする

Clickの動作方式は,何かコールバックを呼び出す前に,プログラマが定義したパラメータ順序とユーザが定義したパラメータ順序を一致させることを試みるため,他の命令行解析器とはやや異なる.

複雑なパターンをoptparseや他のシステムからClickに移植する際には,理解すべき重要な概念である.Optparseにおける引数コールバックは解析手続きの一部であり,Clickにおけるコールバック呼び出しは解析後に行われる.

主な違いは,optparseではコールバックが発生時にオリジナル値を用いて呼び出されるのに対し,Clickにおけるコールバックは値が完全に変換された後に呼び出されることである.

一般に、呼び出し順序は、ユーザがスクリプトにパラメータを提供する順序によって駆動される。 --foo 1つの名前は --bar ユーザはこれを --bar --foo そしてコールバックします bar 最初の射撃の前に発砲します foo それがそうです。

この規則には3つの例外があり、この3つの例外は重要である。

渇望:

オプションを“渇望”に設定することができます。すべての緊急パラメータは、すべての非緊急パラメータの前に値を求めるが、再びユーザがコマンドライン上で提供される順序で値を求める。

これは実行と脱退のパラメータに非常に重要であり,以下のようになる. --help そして --version それがそうです。この2つのパラメータはいずれも切実に必要なパラメータであるが,命令行に最初に現れるパラメータが勝ってプログラムを脱退する.

重複パラメータ:

コマンドライン上のあるオプションまたはパラメータが重複によって複数の位置に分割されている場合、例えば、 --exclude foo --include baz --exclude bar --コールバックは、第1のオプションの位置に応じてトリガされます。この場合コールバックは exclude この2つの選択肢で (foo そして bar )であれば、コールバックします include そうする. baz ただです。

1つのパラメータが複数のバージョンを許可しなくても、Clickは最初のバージョンの位置を受け入れるが、最後のバージョン以外のすべての値は無視されることに留意されたい。このようにした理由は,デフォルト値のshell別名を設定することで組合せ可能であるためである.

欠落パラメータ:

命令行にパラメータが定義されていなければ,コールバックは依然としてトリガされる.これは,optparseにおける動作方式とは異なり,optparseでは,未定義の値がコールバックをトリガしない.欠落したパラメータは、最後にそれらのコールバックをトリガし、これにより、以前のパラメータからの値をデフォルトすることができる。

ほとんどの場合、あなたはこのような問題を心配する必要はありませんが、重要なのはそれがいくつかの高度な状況でどのように働いているのかを知ることです。

転送未知のオプション

場合によっては、さらなる手動処理を行うためにすべての未知のオプションを受け入れることができることは興味深い。Click 4.0から始めて,Clickは通常これを行うことができるが,問題の性質に依存するいくつかの制限がある.これに対する支援は解析器フラグによって提供され,このフラグは ignore_unknown_options これは、解析器がすべての未知のオプションを収集し、解析エラーをトリガするのではなく、残りのパラメータに入れるように指示する。

これは通常2つの異なる方法で活性化することができます

  1. カスタマイズすることができます Command サブクラス、方法は変更です ignore_unknown_options 属性です。

  2. コンテキストクラス上の同名属性を変更することで、この機能を有効にすることができます (Context.ignore_unknown_options )。これは通過したほうがいいです context_settings 命令上の辞書。

ほとんどの場合、最も単純な解決策は二番目のものだ。アクションが変更されると,それらの残りのオプションを処理する必要がある(この点では,これらのオプションはパラメータと考えられる).同様に2つの選択肢があります

  1. ご利用いただけます pass_context() 文脈を通過させることができますこれは以下のような場合にのみ機能する ignore_unknown_options あなたはまた設定しました allow_extra_args そうでなければ,このコマンドは中止され,残りのパラメータのエラーがあることを示す.この解決策を採用すれば、追加のパラメータが収集されます Context.args それがそうです。

  2. 1つを argument() 使用 nargs とする. -1 これは残りのすべての論争を食べるだろう。この場合には type 至る UNPROCESSED これらのパラメータに対して任意の文字列処理を行うことを避けるために、そうでなければ自動的にUnicode文字列に強制的に変換されますが、これは通常あなたが望むものではありません。

最後にこのような結果が得られます

import sys
from subprocess import call

@click.command(context_settings=dict(
    ignore_unknown_options=True,
))
@click.option('-v', '--verbose', is_flag=True, help='Enables verbose mode')
@click.argument('timeit_args', nargs=-1, type=click.UNPROCESSED)
def cli(verbose, timeit_args):
    """A fake wrapper around Python's timeit."""
    cmdline = ['echo', 'python', '-mtimeit'] + list(timeit_args)
    if verbose:
        click.echo(f"Invoking: {' '.join(cmdline)}")
    call(cmdline)

どのように見えるのでしょうか

$ cli --help
Usage: cli [OPTIONS] [TIMEIT_ARGS]...

  A fake wrapper around Python's timeit.

Options:
  -v, --verbose  Enables verbose mode
  --help         Show this message and exit.

$ cli -n 100 'a = 1; b = 2; a * b'
python -mtimeit -n 100 a = 1; b = 2; a * b

$ cli -v 'a = 1; b = 2; a * b'
Invoking: echo python -mtimeit a = 1; b = 2; a * b
python -mtimeit a = 1; b = 2; a * b

ご覧のように詳細なフラグはClickによって処理され、他のすべてのコンテンツは最終的に timeit_args 変数はさらに処理され,次にたとえばサブフローの呼び出しが許可される.未処理のフラグを無視してどのように発生するかについては,いくつかの重要な点がある.

  • 未知の長い整数オプションは一般的に無視され、全く処理されないだろう。例えばもし --foo=bar あるいは…。 --foo bar 普通はこのように終わります。解析器はオプションがパラメータを受け取るかどうかを知ることができないため,注意してください bar 部分はパラメータとして扱うことができる.

  • 未知の空きオプションは部分的に処理され,必要に応じて再結合される可能性がある.例えば、上記の例では、1つの名前がある -v これは詳細なモードを有効にするだろう。このコマンドが無視される場合は、ご利用ください -va そして…。 -v 部品はClick(よく知られている)で処理され、 -a 最終的には,さらなる処理のために残りのパラメータに現れる.

  • 計画された操作によって、配布されたパラメータを無効にすることはいくつか成功するかもしれません (allow_interspersed_args )は、パーサがパラメータおよびオプションの混合を許可しないことを示す。あなたの場合によっては、これはあなたの結果を改善するかもしれません。

一般に、あなた自身のコマンドおよび別のアプリケーションからのコマンドのオプションおよびパラメータを組み合わせて処理することは奨励されていないが、もしあなたがこのような状況を回避することができる場合、あなたはそうすべきである。いくつかのパラメータを自分で処理するよりも,サブコマンド下のすべてのコンテンツを別のアプリケーションに転送した方がよい.

グローバルコンテキストアクセス

Changelog

バージョン 5.0 で追加.

Click 5.0からは get_current_context() 関数はそれを返します。これは、主に、実行時アクションをカスタマイズするために、コンテキストバインディングオブジェクトおよびそれに格納されたいくつかのフラグにアクセスするために使用される。例えば echo() 関数がこのようにするのは推定のためです color 旗。

例示的な用法:

def get_current_command_name():
    return click.get_current_context().info_name

これは現在のスレッドでのみ機能することに注意されたい.追加のスレッドが生成されると、これらのスレッドは現在のコンテキストを参照することができなくなる。別のスレッドがこのコンテキストを参照できるようにするためには、スレッド内のコンテキストをコンテキストマネージャとして使用する必要がある:

def spawn_thread(ctx, func):
    def wrapper():
        with ctx:
            func()
    t = threading.Thread(target=wrapper)
    t.start()
    return t

現在,スレッド関数はメインスレッドのようにコンテキストにアクセスすることができる.しかし、もしあなたがそれをスレッド化に使用する場合、ほとんどの文脈はスレッドが安全ではないので、非常に注意する必要があります!あなたは文脈からしか読み取ることができませんが、それを何の修正も実行することはできません。

パラメータの出所を検出する

場合によっては、オプションまたはパラメータがコマンドライン、環境、デフォルト値、またはコマンドラインから来ているかどうかを理解する Context.default_map それがそうです。♪the Context.get_parameter_source() 方法はこの点を見つけることができる.戻ってきます ParameterSource エンム。

@click.command()
@click.argument('port', nargs=1, default=8080, envvar="PORT")
@click.pass_context
def cli(ctx, port):
    source = ctx.get_parameter_source("port")
    click.echo(f"Port came from {source.name}")
$ cli 8080
Port came from COMMANDLINE

$ export PORT=8080
$ cli
Port came from ENVIRONMENT

$ cli
Port came from DEFAULT

資源を管理する

サブコマンドを利用可能にするためにグループ内のリソースを開くことは有用である可能性がある。多くの種類の資源は使用後に閉鎖されるか、または他の方法で掃除される必要がある。Pythonでこの動作を実行する標準的な方法は、コンテキストマネージャと with 陳述する。

例えば Repo 類は由来する. 複雑なアプリケーション コンテキストマネージャとして定義されるかもしれません:

class Repo:
    def __init__(self, home=None):
        self.home = os.path.abspath(home or ".")
        self.db = None

    def __enter__(self):
        path = os.path.join(self.home, "repo.db")
        self.db = open_database(path)

    def __exit__(self, exc_type, exc_value, tb):
        self.db.close()

一般的には with 声明:

with Repo() as repo:
    repo.db.query(...)

しかし、a with サブコマンドがデータベースを使用することができる前に、グループ内のブロックは終了し、データベースを閉じる。

代わりに文脈を使って with_resource() 方法は、コンテキストマネージャに入り、リソースを返します。グループおよび任意のサブコマンドが完了すると、コンテキストのリソースがクリアされる。

@click.group()
@click.option("--repo-home", default=".repo")
@click.pass_context
def cli(ctx, repo_home):
    ctx.obj = ctx.with_resource(Repo(repo_home))

@cli.command()
@click.pass_obj
def log(obj):
    # obj is the repo opened in the cli group
    for entry in obj.db.query(...):
        click.echo(entry)

リソースがコンテキストマネージャでない場合、一般に、以下の内容を使用してコンテキストマネージャにパッケージ化することができる contextlib それがそうです。これが不可能であれば文脈の call_on_close() メソッドクリーニング関数を登録する.

@click.group()
@click.option("--name", default="repo.db")
@click.pass_context
def cli(ctx, repo_home):
    ctx.obj = db = open_db(repo_home)

    @ctx.call_on_close
    def close_db():
        db.record_use()
        db.save()
        db.close()