Typerはいいぞ


TL;DR

Typer最高!

今すぐpip install typerをしよう!

Typerの使い方だけ見たい場合はこちら

対象読者

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

Example Codeについて

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

Command Line Tool開発のお気持ち

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

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

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

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)

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

現状

想定状況例

図のような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

これとお気持ちで説明した書き方との比較した場合の一番の不満点は、長くなってくると変数を追うのが面倒になってくる点です。
もちろん、変数名や切り出した関数の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

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

$ 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が出てきました!

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

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

まとめ

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

補足

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

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