web-dev-qa-db-ja.com

クリックコマンドから別のクリックコマンドを呼び出す

いくつかの便利な機能をコマンドとして使用したい。そのために、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_nameadd_surnameおよびadd_name_and_surname を使って setup.pyファイルとpip 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_nameおよびadd_surnameコマンドとしてではなく関数として、そうでなければ、ライブラリ関数とコマンドの両方として関数を使用するという私の本来の目的を無効にします。

16
kaligne

クリックデコレータにより、引数を指定するだけでは関数を呼び出すことができなくなります。 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

18
Vikas Tikoo

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モジュール-必要に応じて他のインターフェイスに置き換えることができる「コア」ロジックとクリック固有の実装に配置することをお勧めします。

24
shevron