Fireを使ってPythonスクリプトで引数を受け取る

pythonでCliアプリケーションを作成する際、コマンドライン引数を名前付きで与えたくていろいろ調べたところ、[Fire](https://github.com/google/python-fire)というパッケージを発見しました。
sys.argvとかargparseとかclickとかありましたが、もっともすっきりかけるのがFireかと思います。

# 早速実装

とりあえずベースはこんな感じで

```python:test1.py
def main() -> None:
    print("mainです")


if __name__ == "__main__":
    main()
```

実行

```console
python test1.py
```

結果

```console
mainです
```


## Fireを使ってみる

```python:test2.py
import fire


def main() -> None:
    print("mainです")


if __name__ == "__main__":
    fire.Fire(main)
```

実行

```sh
python test2.py
```

結果

```console
mainです
```

実行は無事できました。

## 引数を与えてみる

```python:test3.py
import fire


def main(message: str) -> None:
    print(message)
    print(type(message))


if __name__ == "__main__":
    fire.Fire(main)
```

messageという名前で文字列を受け取り、その文字列と型をprintするようにしてみました。

まずはこれまで同様、引数なしで実行してみましょう。

```sh
python test3.py
```

結果

```console
ERROR: The function received no value for the required argument: message
Usage: test3.py MESSAGE

For detailed information on this command, run:
  test3.py --help
```

引数が不足している旨のメッセージが表示されました。
`--help`で詳細が見れると書いてありますね。見てみましょう。

```sh
python test3.py --help
```

結果

```console
NAME
    test3.py

SYNOPSIS
    test3.py MESSAGE

POSITIONAL ARGUMENTS
    MESSAGE
        Type: str

NOTES
    You can also use flags syntax for POSITIONAL ARGUMENTS
```

vimっぽい状態で上記内容が表示されました。`q`で抜けられます。
ファイル名と概要、引数の名称と型、位置引数にフラグ？を使用することもできます的なメッセージが表示されてます。
ちなみにこれは`-h`の短縮形でも表示されました。

引数を与えて実行してみます。

```sh
python test3.py --message=こんにちは
```

```console
こんにちは
<class 'str'>
```

成功です！

## Optional Argumentsの追加

```python:test4.py
import fire


def main(message: str, suuji: int = 0) -> None:
    print(message)
    print(suuji)


if __name__ == "__main__":
    fire.Fire(main)
```

suujiというint型のデフォルト引数を追加してみました。
helpを見てみます。

```sh
python test4.py -h
```

```console
NAME
    test4.py

SYNOPSIS
    test4.py MESSAGE <flags>

POSITIONAL ARGUMENTS
    MESSAGE
        Type: str

FLAGS
    --suuji=SUUJI
        Type: int
        Default: 0

NOTES
    You can also use flags syntax for POSITIONAL ARGUMENTS
```

FLAGSとしてsuujiが追加されました。NOTESに書いてあるflag syntaxというやつになったということでしょうか？
とりあえず実行してみます。

```sh
python test3.py --message=こんにちは
```

結果

```console
こんにちは
10
```

思った通りには実行されてますね。

## 名前付きでの実行を強制する

helpで見たときに`POSITIONAL ARGUMENTS`として定義されている通り、これは位置引数としても定義されています。

```sh
python test4.py こんにちは 1
```

```console
こんにちは 
1
```

折角なので名前付き引数であることを強制してみます。

Pythonで関数定義をする際、`*`より後ろは名前付き引数であることを強制できるのでそれでやってみましょう。

```python:test5.py
import fire


def main(*, message: str, suuji: int = 0) -> None:
    print(message)
    print(suuji)


if __name__ == "__main__":
    fire.Fire(main)
```

helpを見てみます。

```sh
python test5.py -h
```

結果

```console
NAME
    test5.py

SYNOPSIS
    test5.py <flags>

FLAGS
    --message=MESSAGE (required)
        Type: str
    --suuji=SUUJI
        Type: int
```

両方`FLAGS`のほうに行きましたね。

先ほどの位置引数指定バージョンで実行してみます。

```console
ERROR: Missing required flags: {'message'}
Usage: test5.py <flags>
  optional flags:        --message
  required flags:        --suuji

For detailed information on this command, run:
  test5.py --help
```

ちゃんとエラーとなりました。

名前付きなら無事実行できます。

```sh
python test5.py  --message=こんにちは --suuji=1
```

```console
こんにちは 
1
```

# まとめ

argparse使ってたことがありますが、それと比較するとめちゃくちゃ楽ですね。
ちょっとしたCLIアプリケーションを作るときPythonは便利なので、これでできることが広がります。