はじめに
マルチコマンド・チェイニング!1 nikkieです。
コマンドラインツール作成は標準ライブラリのargparseが多い私2ですが、サードパーティのClick(Command Line Interface Creation Kitの頭辞語)も覗いてみました。
「argparseではできなさそうですごいな〜」と思ったMulti Command Chainingについてドキュメントで理解したことを綴ります。
目次
動作環境
- Python 3.11.4
- Click 8.1.3
- ドキュメントも8.1.xを参照します。 https://click.palletsprojects.com/en/8.1.x/
ClickのMulti Command
おことわり:ClickのCommandは知っている前提です
ドキュメントのQuickstartの例ですが、@click.command()
で関数をデコレートすると、CLIのコマンドにできます!
このデコレータにより、内部ではhello
関数がCommandになっています。
import click @click.command() def hello(): click.echo('Hello World!') if __name__ == '__main__': hello()
$ python hello.py Hello World!
FYI: Clickの使い出しをお助けすると思われる情報リストです。
- ドキュメントのQuickstart(英語)
- 上記のhelloコマンドの後に、引数の追加の仕方が続きます。「Adding Parameters」
- なんで
print
じゃなくてclick.echo
なの? 👉 「Echoing」3
- 『Python実践入門』13章
- Clickも使って、
lgtm
コマンドを作ります - 実装 https://github.com/rhoboro/lgtm
- Clickも使って、
- 『Python実践レシピ』10.4.3
- argparseと類似するツールとして紹介されますが、一瞬です
ドキュメントの「Callback Invocation」の例
Clickでは複数のCommandをGroupにまとめられます。
複数のコマンドはMultiCommandという概念らしいのですが、そのもっともありふれたバージョンがGroupになります4。
例はこちらから:https://click.palletsprojects.com/en/8.1.x/commands/#callback-invocation
- Group
cli
を定義 @cli.command()
というデコレータにより、Groupの中にsync
Commandを用意します
(以下で使っているtoolpy
コマンドへのまとめ方は後述します)
% toolpy --help Usage: toolpy [OPTIONS] COMMAND [ARGS]... Options: --debug / --no-debug --help Show this message and exit. Commands: sync
この例はGroupとCommandのコールバック(=プログラマが書いた関数)の呼び出し順を確認するためのものです。
% toolpy --debug sync
Debug mode is on
Syncing
% toolpy sync
Debug mode is off
Syncing
ポイントは、Group cli
のコールバック(cli
関数)が常に実行されていること!
- コマンドのユーザが
toolpy --debug sync
と呼び出し5 - コマンドはまずGroupのコールバックである
cli
関数を呼び出す - その後、Commandのコールバックである
sync
関数を呼び出す
実際はGroupのコールバックをpass
(何もしない)と実装する事が多いと思います。
Group -> Commandの順でコールバックが発火というのが学びでした。
pyproject.tomlを使ってコマンドにする!
ドキュメント「Setuptools Integration」には、python yourscript.py
と実行するのではなく、setup.pyを用意してpip install --editable .
してyourscript
コマンドとして使えるようにしよう、とあります。
コミュニティとしては、setup.pyよりはpyproject.tomlを使っていく流れ6と理解しているので、pyproject.tomlを書いてtoolpy
コマンドを設定しました。
思うにドキュメントがtool.py
と実行しているのは、Pythonファイルに実行権限が付いていて、シバン(#!/usr/bin/env python
)を使っているんじゃないでしょうか。
私はpyproject.tomlを使う方法を推していきます。
動作環境の追加情報です。
- pip 23.1.2
- setuptools 68.0.0
Multi Command Chaining
https://click.palletsprojects.com/en/8.1.x/commands/#multi-command-chaining
(Groupにまとめた)複数のコマンドをチェーン(連鎖)させられます!
連鎖のイメージは、setup.py sdist bdist_wheel upload
7。
setup.py sdist
setup.py bdist_wheel
8setup.py upload
という連鎖を、コマンド呼び出し1回で実行できます!
ドキュメントにある例です。
ポイントは@click.group(chain=True)
!
pyproject.toml
は以下を変えています。
- projectのname
- tool.setuptoolsのpy-modules
- project.scripts
setuppy = "chaining_example:cli"
ヘルプメッセージが見えるのでインストールOK!
% setuppy --help Usage: setuppy [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]... Options: --help Show this message and exit. Commands: bdist_wheel sdist
連鎖することを確認しましょう。
% setuppy sdist sdist called % setuppy bdist_wheel bdist_wheel called % setuppy sdist bdist_wheel sdist called bdist_wheel called % setuppy bdist_wheel sdist bdist_wheel called sdist called
連鎖できてます!🙌
終わりに
Clickの「Multi Command Chaining」についてドキュメントの例をまとめました。
- ClickでMulti Commandのよくある例がGroup
- Groupのコールバック -> Commandのコールバックの順で呼び出される
- Groupのコールバックの実装は
pass
をよく見る(毎回呼ばれるけれど、何もしない)
- Groupのコールバックの実装は
@click.group(chain=True)
で、コマンド利用者はグループにまとめたコマンドを連鎖させて呼び出せる
また「Multi Command Chaining」以外では以下もアウトプットしました。
- setup.pyの代わりにpyproject.tomlを使って、Clickで実装したコマンドをsetuptoolsで環境にインストールする
ドキュメントの例は完全に理解したので、引数の扱いはどうなるのかなど、引き続き素振りしていこうと思います。
- Chainingとingで終わるので、語感がSAOのアリシゼーション編っぽいなと思いました。「アリシゼーション・ライジング」!など↩
- https://nikkie-ftnext.hatenablog.com/search?q=argparse↩
- もっと詳しくUtilitiesのドキュメントにあります。https://click.palletsprojects.com/en/8.1.x/utils/#printing-to-stdout↩
- 「The most common version is the Group.」 ref: https://click.palletsprojects.com/en/8.1.x/api/#click.MultiCommand↩
-
細かい点ですが、
--debug
オプションはcli
Groupの引数なので、toolpy sync --debug
(sync Commandに--debug
オプションが渡る)とはできません↩ - 「New projects are advised to avoid setup.py configurations」 ref: https://setuptools.pypa.io/en/latest/userguide/quickstart.html#setup-py↩
-
この
setup.py
も実行可能なPythonスクリプトだと思います↩ -
最近は
python -m build .
でめっちゃ楽しています(build)。みんなも楽してこーぜ!↩