シェルスクリプトで頑張って作っていたパラメータの解析も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
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でコマンドを作ろうと思う。
参考
click.palletsprojects.com
kotapontan.hatenablog.com