nikkie-ftnextの日記

イベントレポートや読書メモを発信

Clickは複数のコマンドを連鎖させて呼び出せる!(awesome_cli command_a command_b command_c) ドキュメント「Multi Command Chaining」の例を完全に理解しました

はじめに

マルチコマンド・チェイニング!1 nikkieです。

コマンドラインツール作成は標準ライブラリのargparseが多い私2ですが、サードパーティのClick(Command Line Interface Creation Kitの頭辞語)も覗いてみました。
「argparseではできなさそうですごいな〜」と思ったMulti Command Chainingについてドキュメントで理解したことを綴ります。

目次

動作環境

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章
  • 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 upload7

  1. setup.py sdist
  2. setup.py bdist_wheel8
  3. setup.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をよく見る(毎回呼ばれるけれど、何もしない)
  • @click.group(chain=True)で、コマンド利用者はグループにまとめたコマンドを連鎖させて呼び出せる

また「Multi Command Chaining」以外では以下もアウトプットしました。

  • setup.pyの代わりにpyproject.tomlを使って、Clickで実装したコマンドをsetuptoolsで環境にインストールする

ドキュメントの例は完全に理解したので、引数の扱いはどうなるのかなど、引き続き素振りしていこうと思います。


  1. Chainingとingで終わるので、語感がSAOのアリシゼーション編っぽいなと思いました。「アリシゼーション・ライジング」!など
  2. https://nikkie-ftnext.hatenablog.com/search?q=argparse
  3. もっと詳しくUtilitiesのドキュメントにあります。https://click.palletsprojects.com/en/8.1.x/utils/#printing-to-stdout
  4. The most common version is the Group.」 ref: https://click.palletsprojects.com/en/8.1.x/api/#click.MultiCommand
  5. 細かい点ですが、--debugオプションはcli Groupの引数なので、toolpy sync --debug(sync Commandに--debugオプションが渡る)とはできません
  6. New projects are advised to avoid setup.py configurations」 ref: https://setuptools.pypa.io/en/latest/userguide/quickstart.html#setup-py
  7. このsetup.pyも実行可能なPythonスクリプトだと思います
  8. 最近はpython -m build .でめっちゃ楽しています(build)。みんなも楽してこーぜ!