シェルスクリプトで頑張って作っていたパラメータの解析もPythonのclick.commandを使えば簡単に実装できるし、コマンドで必要なことがモジュールとして提供されている。
シェルスクリプトから脱却して、Pythonでコマンドを作ろうぜ。
フォントの色、スタイルを変更できる
style()関数を使用することで、フォントの色やスタイルを変更が可能。
click.echo(click.style('Hello World!', fg='green')) click.echo(click.style('Some more text', bg='blue', fg='white')) click.echo(click.style('ATTENTION', blink=True, bold=True))
ヘルプテキストの記載が簡単
シェルスクリプトのヘルプメッセージは地味に大変。
click.commandだと関数のdocstringが指定されている場合、自動的にdocstringの内容が使用される。
@click.command() @click.argument("name") def hello(name: str): """docstringの内容がヘルプに指定されるよ""" click.echo("name: " + name )
Usage: clickcmd_sample.py [OPTIONS] NAME docstringの内容がヘルプに指定されるよ Options: --help Show this message and exit.
環境変数
auto_envvar_prefixに環境変数のプレフィックスを設定する必要がある。
auto_envvar_prefixに指定したプレフィックスとアンダーバーを除いた文字列が変数名になる。
以下の場合、HELLO_USERNAMEとHELLO_JOBが環境変数となり、環境変数の値が格納される変数名は、usernameとjobになる。
@click.command() @click.option('--username') @click.option("--job", envvar="JOB") def hello(username, job): click.echo("username = " + str(username)) click.echo("job = " + str(job)) if __name__ == "__main__": hello(auto_envvar_prefix="HELLO")
# export HELLO_USERNAME=sato # export HELLO_JOB=engineer # python clickcmd_sample.py username = sato job = engineer
ユーザー入力のためのプロンプト表示も
コマンドのパラメータ指定ではなく、ユーザーに値を入力させたいケースがある。
そのケースもclick.command
は対応している。
click.option
デコレーターのpromptパラメータにTrueを指定するだけ。
import click @click.command() @click.option('--brithplace', prompt=True) def hello(brithplace): click.echo("brithplace : " + str(brithplace)) if __name__ == "__main__": hello()
プロンプトのメッセージもカスタマイズをする場合、promptパラメータに表示したいメッセージを指定する。
import click @click.command() @click.option('--brithplace', prompt="あなたの出身地は?") def hello(brithplace): click.echo("入力した出身地 : " + str(brithplace)) if __name__ == "__main__": hello()
あなたの出身地は?: 神奈川 入力した出身地 : 神奈川
パスワード入力も簡単に実装できる。
hide_inputパラメータをTrueに設定し、入力を非表示にする。
また、confirmation_promptで確認用に再入力をさせる。
試してみて分かったことは、パスワード入力を数回間違えれば、コマンドが終了するのではなく、ずっとパスワード入力が求められるということ。
回数指定出来たらいいのに。
import click @click.command() @click.option('--password', prompt="パスワードを入力してください", hide_input=True, confirmation_prompt=True) def hello(password): click.echo("入力したパスワード : " + str(password)) if __name__ == "__main__": hello()
テストも簡単
テスト用のモジュールも提供されているので、テストが容易。
以下をテストをする。
import click @click.command() @click.argument("brithplace") @click.argument("schoolname") def show_your_birthplace(brithplace, schoolname): click.echo("brithplace = " + str(brithplace)) click.echo("schoolname = " + str(schoolname)) if __name__ == '__main__': show_your_birthplace()
CliRunnerクラスのオブジェクトを生成し、invoke関数を実行する。
第一パラメータにテスト対象の関数、第二パラメータにパラメータを配列で指定。
invoke関数の戻り値に結果が格納される。
標準出力はresult.output
に格納される。改行含めての文字列になっているので、assertするときは改行で分割したほうが、チェックしやすい。
from click.testing import CliRunner from click_easy import show_your_birthplace def test_show_your_birthplace(): runner = CliRunner() result = runner.invoke(show_your_birthplace, ["Kanagawa", "WAHAHA"]) assert result.exit_code == 0 assert result.output == "brithplace = Kanagawa\nschoolname = WAHAHA\n"
ユーザーの入力させる処理もinput変数を使用すればテスト可能。
これは嬉しい。
import click @click.command() @click.option("--brithplace", prompt="出身は?") @click.option("--schoolname", prompt="出身校は?") def show_your_birthplace(brithplace, schoolname): click.echo("brithplace = " + str(brithplace)) click.echo("schoolname = " + str(schoolname)) if __name__ == '__main__': show_your_birthplace()
input変数に入力する値を設定。
output変数には入力を求める文字列も含まれるので、合わせて試験が可能。
from click.testing import CliRunner from click_easy import show_your_birthplace def test_show_your_birthplace(): runner = CliRunner() result = runner.invoke(show_your_birthplace, input="Kanagawa\nWAHAHA\n") assert result.exit_code == 0 #assert result.output == "出身は?: Kanagawa\n出身校は?: WAHAHA\nbrithplace = Kanagawa\nschoolname = WAHAHA\n" outputs = result.output.split('\n') assert len(outputs)-1 == 4 # 末尾の改行を除く assert outputs[0] == "出身は?: Kanagawa" assert outputs[1] == "出身校は?: WAHAHA" assert outputs[2] == "brithplace = Kanagawa" assert outputs[3] == "schoolname = WAHAHA" assert outputs[4] == ""
さいごに
コマンドはほぼシェルスクリプトで作成しているので、単体テストが後回しになったり、全ルートを通すために苦労する。
それにシェルスクリプトのテストツールも少ない。そもそも、シェルスクリプトのテストツールを使っているところ自体が少ない。
それに比べて、Pythonでコマンドに必要なことがモジュールで提供されているので、実装も楽だし、テストも容易。これはPythonでコマンド作ろぜってことだ。
自分のプロジェクトでも機会があれば、Pythonでコマンドを作ろうと思う。