Typerはいいぞ

# TL;DR
### Typer最高！  
###今すぐ```pip install typer```をしよう！

Typerの使い方だけ見たい場合は[こちら](#typerを使う)

# 対象読者
- これから初めてcliツールの作成をする方
- argparseのalternativeを探している方
- argparseにどこか不満を感じている方

# Example Codeについて
皆さんはcliツール作成と聞いて何を思い浮かべるのでしょうか？
例として挙げるならば、入力自動化ツール、APIコールするだけのツール、パブリッククラウドの設定更新ツールなど様々なものがあると思います。
本記事では、実務でも役に立って欲しいとの観点から、初心者ソフトウェアエンジニアが業務として書きがち[要出典]な[Beam Pipeline](https://beam.apache.org/releases/pydoc/2.22.0/)を例として取り上げます。

# Command Line Tool開発のお気持ち
そもそも、お気持ちとしてどのようなツールをどういう風に開発したいのでしょうか？
私は、ユーザーフレンドリーなツールを、読みやすいコードで、尚且つ少ない記述量で開発がしたいです。
短いコードというのは往々にして読みやすく、可読性が向上すると私は思っているので、実質的には、

- ユーザーフレンドリーである
- 可読性が高く、保守性が高い

の2点を重視したいと考えています。
つまり、お気持ちとして、

```python
def my_awesome_command(op1, op2, op3):
    # do whatever you want
    return
```

こういうコードで、使用者側は、

```
my_awesome_command --op1 1 --op2 2 --op3 3
```

のように使用することができて、

```
my_awesome_command -h
```

のようにヘルプを呼ぶと、

```
usage: my_awesome_command [OPTIONS]

options:
  --op1         awesome option1(int) [default:1](required)
  --op2         awesome option2(int) [default:2](required)
  --op3         awesome option3(int) [default:3](not required)
```

のような説明が出てきて欲しいです。

# 現状
## 想定状況例
<img width="613" alt="スクリーンショット 2020-07-02 16.01.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510871/02f9bdde-29bd-791d-5d5b-a277063a8561.png">

図のようなPub/Subで受けてBigQueryに流すだけの簡単なData Pipelineについて考えます。
## 書きがちなコード

```flow.py

    parser = argparse.ArgumentParser(description='Awesome Pipeline')
    parser.add_argument('--project', required=True,
                        help='Your Google Cloud Project')
    parser.add_argument('--topic', required=True, help='Your PubSub topic')
    parser.add_argument('--bucket', required=True,
                        help='Your tmp/test Input GCS')
    parser.add_argument('--dataset', required=True,
                        help='Your Output BigQuery Dataset')

    ...(omitted)...

    settings = [
        f'--project={args.project}',
        '--job_name=job1',
        f'--staging_location=gs://{args.bucket}/staging/',
        f'--temp_location=gs://{args.bucket}/tmp/',
        f'--runner={runner}',
        '--region=asia-northeast1',
        '--max_num_workers=1'
    ]

    ...(omitted)...
```
[Full Code](https://github.com/wubw555/beam_template/blob/master/argparse_sample/flow.py)

これと[お気持ち](#command-line-tool開発のお気持ち)で説明した書き方との比較した場合の一番の不満点は、長くなってくると変数を追うのが面倒になってくる点です。
もちろん、変数名や切り出した関数のdocstringである程度カバーはできますが、変数のtype情報や定義を探すのにエディタの機能がうまく使えず、結局手動で宣言場所を見に行くという作業が発生してしまうことも多いです。
長期的に見るとストレスが積み重なってしまう結果になることがあります。

# Typerを使う

それでは、Typerを用いた場合の[前述のコード](#書きがちなコード)の書き方をみてみましょう。

```flow.py
def main(
    project: str = Option(..., help='Your Google Cloud Project'),
    topic: str = Option(..., help='Your PubSub Topic'),
    bucket: str = Option(..., help='Your tmp/test Input GCS'),
    dataset: str = Option(..., help='Your Output BigQuery Dataset'),
    table: str = Option(..., help='Your Output BigQuery Table'),
    runner: bool = Option(..., '--DirectRunner/--DataflowRunner',
                          help='Your Pipeline Runner'),
):
    '''
    Awesome Pipeline
    '''

    ...(omitted)...

    settings = [
        f'--project={project}',
        '--job_name=job1',
        f'--staging_location=gs://{bucket}/staging/',
        f'--temp_location=gs://{bucket}/tmp/',
        f'--runner={beam_runner}',
        '--region=asia-northeast1',
        '--max_num_workers=1',
    ]

    ...(omitted)...
```
[Full Code](https://github.com/wubw555/beam_template/blob/master/typer_sample/flow.py)

このように書き、helpを呼ぶと以下のように表示されます。

```terminal
$ python flow.py --help

Usage: flow.py [OPTIONS]

  Awesome Pipeline

Options:
  --project TEXT                  Your Google Cloud Project  [required]
  --topic TEXT                    Your PubSub Topic  [required]
  --bucket TEXT                   Your tmp/test Input GCS  [required]
  --dataset TEXT                  Your Output BigQuery Dataset  [required]
  --table TEXT                    Your Output BigQuery Table  [required]
  --DirectRunner / --DataflowRunner
                                  Your Pipeline Runner  [required]
  --install-completion [bash|zsh|fish|powershell|pwsh]
                                  Install completion for the specified shell.
  --show-completion [bash|zsh|fish|powershell|pwsh]
                                  Show completion for the specified shell, to
                                  copy it or customize the installation.

  --help                          Show this message and exit.
```
**とても分かりやすいhelpが出てきました!**

ご覧の通り、[お気持ち](#command-line-tool開発のお気持ち)で説明した内容を達成できているのが確認できると思います。
これで、長くなったとしてもエディタの機能で引数の定義に飛ばずともtype情報は取得できますし、詳細が知りたくなった時にはすぐに欲しい引数の定義に飛ぶことができるようになりました！

加えて、見慣れた慣習に沿っていることが確認できると思います。
これなら、argparseのために新しいルールを導入しなくて済み便利ですね！

# まとめ
**Typerを使ってみたくなりましたか？**
気づかれた方も多いと思うのですが、Typerには使用時のコード補完をインストールする機能もあります！
この記事では個々のコンポーネントは割愛させて頂くのですが、気になった方は**なんと**[公式ドキュメント](https://typer.tiangolo.com/)[^1]を読むことで全ての機能の使い方を知ることができます！
使い方が気になった方はぜひ読んでみましょう。
この記事でTyperを使ってくれる人が増えることを祈っています🙏

# 補足
蛇足かもしれませんが、この作者は[FastAPI](https://fastapi.tiangolo.com/)[^2]と同じ作者です。
FastAPIも大変便利で素晴らしいFrameworkですので、まだ見たことがない方は是非一度見てみることをお勧めします。
Typerは現時点(2020/09/06)でqiita上に1記事もなかった😭のですが、FastAPIの方はたくさん書かれている方がいらっしゃいますので、是非検索してみてください。

また、もしコード、または説明に誤りを発見された方がいらっしゃった場合にはコメントに残しておいてくれたら大変助かります。
筆者は初心者エンジニアの🐈なのですぐにというわけにはいかないかもしれませんが、可能な限り早く修正いたします。

[^1]: [Typer公式ドキュメント](https://typer.tiangolo.com/)
[^2]: [FastAPI公式ドキュメント](https://fastapi.tiangolo.com/)