49

便利な機能をコマンドとして使いたい。そのために、clickライブラリをテストしています。3 つの元の関数を定義し、次のように装飾しましたclick.command

import click
import os, sys

@click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=True)
def add_name(content, to_stdout=False):
    if not content:
        content = ''.join(sys.stdin.readlines())
    result = content + "\n\tadded name"
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result


@click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=True)
def add_surname(content, to_stdout=False):
    if not content:
        content = ''.join(sys.stdin.readlines())
    result = content + "\n\tadded surname"
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result

@click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=False)
def add_name_and_surname(content, to_stdout=False):
    result = add_surname(add_name(content))
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result

このようにして、3 つのコマンドを生成add_nameし、ファイルをadd_surname使用add_name_and_surnameしてパイプすることができます。setup.pypip install --editable .

$ echo "original content" | add_name | add_surname 
original content

    added name
    added surname

ただし、さまざまなクリック コマンドを関数として作成する場合、解決しなければならない小さな問題が 1 つあります。

$echo "original content" | add_name_and_surname 
Usage: add_name_and_surname [OPTIONS] [CONTENT]

Error: Got unexpected extra arguments (r i g i n a l   c o n t e n t 
)

なぜ機能しないのかわかりません。このコマンドをコマンドとしてではなく関数としてadd_name_and_surname呼び出す必要があります。そうしないと、関数をライブラリ関数とコマンドの両方として使用するという当初の目的が無効になります。add_nameadd_surname

4

4 に答える 4

53

別の関数から直接呼び出すadd_name()add_surname()、実際にはそれらの装飾されたバージョンを呼び出すため、期待される引数はそれらを定義したものとは異なる場合があります (理由の詳細については、Python で関数からデコレータを削除する方法への回答を参照してください)。

元の関数を装飾せずに保持し、それらの薄いクリック固有のラッパーを作成するように、実装を変更することをお勧めします。次に例を示します。

def add_name(content, to_stdout=False):
    if not content:
        content = ''.join(sys.stdin.readlines())
    result = content + "\n\tadded name"
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result

@click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=True)
def add_name_command(content, to_stdout=False):
    return add_name(content, to_stdout)

その後、これらの関数を直接呼び出すか、setup.py によって作成された CLI ラッパー スクリプトを介して呼び出すことができます。

これは冗長に思えるかもしれませんが、実際にはおそらく正しい方法です: 1 つの関数はビジネス ロジックを表し、もう 1 つの関数 (クリック コマンド) は、コマンド ラインを介してこのロジックを公開する "コントローラー" です (目的のために、存在する可能性があります)。たとえば、Web サービスを介して同じロジックを公開する関数など)。

実際、それらを個別の Python モジュール (「コア」ロジックと、必要に応じて他のインターフェイスに置き換えることができるクリック固有の実装) に配置することをお勧めします。

于 2016-10-17T19:43:05.170 に答える
50

クリックデコレーターにより、引数を指定するだけでは関数を呼び出すことができなくなりました。Contextクラスは、ここではあなたの友達です。具体的には:

  1. Context.invoke() - 指定した引数で別のコマンドを呼び出します
  2. Context.forward() - 現在のコマンドの引数を入力します

したがって、add_name_and_surname のコードは次のようになります。

@click.command()
@click.argument('content', required=False)
@click.option('--to_stdout', default=False)
@click.pass_context
def add_name_and_surname(ctx, content, to_stdout=False):
    result = ctx.invoke(add_surname, content=ctx.forward(add_name))
    if to_stdout is True:
        sys.stdout.writelines(result)
    return result

参考: http ://click.pocoo.org/6/advanced/#invoking-other-commands

于 2016-10-17T23:00:32.780 に答える