clizeでCLIを楽々作成

こないだ、[clize](https://clize.readthedocs.io/en/stable/index.html)というpythonライブラリを見つけました。
なかなかいいライブラリだったので、紹介します。

このclizeですが、何をしてくれるのかというと、CLIの簡単作成です。
「いやいや、[fire](https://github.com/google/python-fire)で間に合ってますけど。」って思った方もいると思います。

今回は、pythonでCLIを作る際によく使うライブラリを比較しつつ、clizeについて紹介できればと思います。

# python CLIライブラリ比較

python CLIライブラリは、clizeを含めて下の3つを押さえておけばいいのかなと勝手に思っています。

1. [argparse](https://docs.python.org/ja/3/library/argparse.html)
2. [fire](https://github.com/google/python-fire)
3. [clize](https://clize.readthedocs.io/en/stable/index.html)

この3つを、簡単な算数のスクリプトを作成するサンプルで比較していきます。

## 1. argparse

argparseは、pythonの標準ライブラリです。
標準ライブラリと考えるとよくできていますが、コード量が多く、「ん？これなんだっけ？」となりがちです。

コードはこんな感じになります。

```argparse-sample.py
def main(a: int, b: int, c: float=0.5):
    """
    a × b × c を実行し、結果を返す。

    :param a: 整数を入力してください。
    :param b: 整数を入力してください。
    :param c: 浮動小数点数を入力してください。何も入力しなければ、自動的に0.5が選択されます。
    :return: a × b × c
    """
    return a * b * c

if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument("a", type=int,
                        help="整数aを入力してください。")
    parser.add_argument("b", type=int,
                        help="整数bを入力してください。")
    parser.add_argument("c", type=int, default=0.5,
                        help="浮動小数点数を入力してください。何も入力しなければ、自動的に0.5が選択されます。")

    args = parser.parse_args()

    main(args.a, args.b, args.c)
```

定義も面倒ですし、値を取得する際に`args.a`などと書く必要があり、さらに面倒です。
`main(a, b, c)`の引数が仕様変更された場合などに、「えーっと、なんだっけ？」となりがちです。

helpはこんな感じ。

```
$ python argparse-sample.py --help

usage: sample.py [-h] a b c

positional arguments:
  a           整数aを入力してください。
  b           整数bを入力してください。
  c           浮動小数点数を入力してください。何も入力しなければ、自動的に0.5が選択されます。

optional arguments:
  -h, --help  show this help message and exit
```

## 2. fire

続いてのfireは、Google製のライブラリです。
これが一番使われているのかなと勝手に思っています。

argparseに比べて、「CLI生成を自動化しているぞ！」という気持ちになりやすいです。

```fire-sample.py
def main(a: int, b: int, c: float=0.5):
    """
    a × b × c を実行し、結果を返す。

    :param a: 整数を入力してください。
    :param b: 整数を入力してください。
    :param c: 浮動小数点数を入力してください。何も入力しなければ、自動的に0.5が選択されます。
    :return: a × b × c
    """
    return a * b * c

if __name__ == '__main__':
    import fire
    fire.Fire(main)

```

`fire.Fire(main)`だけで、関数を丸ごとCLI化することができます。
また、クラスについても、`class MyClass(fire.Fire)`とするだけで、全メソッドをCLI化することができます。

ただ、不便な点もあります。

- `-- --help`という変なヘルプの出力方法。
- 変数の説明を表示できない。多分。（知ってたら教えてください。。。）
- 変数の型を勝手に変えてきて、時々うざい。

ヘルプはこんな感じ。

```
$ python fire-sample.py -- --help

Type:        function
String form: <function main at 0x108d67e18>
File:        ~/Repos/projects/kito-xgx-autotuning/bin/sample.py
Line:        38
Docstring:   a × b × c を実行し、結果を返す。

:param a: 整数を入力してください。
:param b: 整数を入力してください。
:param c: 浮動小数点数を入力してください。何も入力しなければ、自動的に0.5が選択されます。
:return: a × b × c

Usage:       sample.py A B [C]
             sample.py --a A --b B [--c C]

```

docstringが表示されるので、申し訳程度に説明を書くことはできますが。。。
また、型の自動変換についても、クセが強いです。
[公式](https://google.github.io/python-fire/guide/#argument-parsing)からのコピペですが、例えばこんな動き。

```example.py
import fire
fire.Fire(lambda obj: type(obj).__name__)
```

```
$ python example.py 10
int
$ python example.py 10.0
float
$ python example.py hello
str
$ python example.py '(1,2)'
tuple
$ python example.py [1,2]
list
$ python example.py True
bool
$ python example.py {name: David}
dict
```

```
$ python example.py 10
int
$ python example.py "10"
int
$ python example.py '"10"'
str
$ python example.py "'10'"
str
$ python example.py \"10\"
str
```

役立つこともあるんですが、たまにうざい。
古いバージョンでは四則演算機能までついていたので、日付を`2019-10-25`のように書くと、引き算されて`1984`になってしまったこともありました。

## 3. clize

そして、本題のclize。clizeのいいところは、下の通り。

- fireとほぼ同じ感覚でCLI自動作成をできる。
- `--help`や`-h`でヘルプが出る。
- docstringをパースし、ヘルプに表示してくれる。
- type annotationを見て、型を変換してくれる。

もちろん、ダメなところもあります。

- `--a 10`や`-a 10`のような書き方ができず、常に`10 20 abc`のような書き方しかできない。

結構致命的ですね。`--a 10`や`-a 10`のフォーマットに対応してくれたら最高なのに。追加してPR出そうかな。。。

コードはこんな感じ。

```clize-sample.py
def main(a: int, b: int, c: float=0.5):
    """
    a × b × c を実行し、結果を返す。

    :param a: 整数を入力してください。
    :param b: 整数を入力してください。
    :param c: 浮動小数点数を入力してください。何も入力しなければ、自動的に0.5が選択されます。
    :param d: 文字列を入力してください。何も入力しなければ、自動的にabcが選択されます。
    :return: a × b × c
    """
    return a * b * c


if __name__ == '__main__':
    from clize import run
    run(main)
```

ヘルプはというと、

```
$ python clize-sample.py --help
Usage: bin/sample.py a b [c]

a × b × c を実行し、結果を返す。

Arguments:
  a            整数を入力してください。 (type: INT)
  b            整数を入力してください。 (type: INT)
  c            浮動小数点数を入力してください。何も入力しなければ、自動的に0.5が選択されます。 (type: FLOAT, default: 0.5)

Other actions:
  -h, --help   Show the help
```

docstringをパースし、typeや説明を表示してくれています。
このhelp画面が最高です。

# まとめ

最後に3ライブラリの特徴をまとめると、

1. argparse: 公式ライブラリ。コード量が多い。
2. fire: 簡単。ただし、ヘルプや型などクセが強い。
3. clize: 簡単。docstringやtype annotationを使ってくれる。ただし、`--a`のような書き方ができない。

いかがでしたか？あれ、「案外clizeいいやつじゃん」と思っていただけた方がいれば幸いです。
「clizeに`-a`つけるぞ！」って方がいれば、ぜひ頑張って欲しいです。

以上、clizeの紹介でした。これ誰が読むんやろ。