CLIアプリケーションのためのTyper
Typerとは
TyperはCLIアプリケーションを作成するためのライブラリです.
Typerの特徴は大きく以下になります.
- 直感的なコーディング
- 使いやすさ
- 少ない量のコーディング
- 導入の簡単さ
- 拡張性
省コストと拡張性を備えたライブラリで,シンプルで使いやすいが故に複雑なカスタマイズができないのかと思いきや,必要に応じてオプション引数やサブコマンドなどの複雑さを備えたCLIアプリケーションを作成したい場合にも対応できるようになっています.
現在の最新バージョンは 0.3.2
となっており,Pythonは3.6以上が対応している様です.より詳細な情報は公式ドキュメントを参照してみてください.
インストール
実行環境は以下となっています.
$ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G6042 $ python -V Python 3.9.1
ライブラリのインストールはpipで簡単に行えます.
$ pip install typer
使い方・実装方法
早速Typerの実装方法を説明していきたいと思います.
CLI Arguments
最初はAugmentsによる引数の利用法です.
Typerでは以下の様に実装していきます.
typer.run
メソッドで引数に実行したい関数を指定してあげるだけです.その際に,実行する関数(main)で定義した引数(name)がコマンドラインで実行する時の引数かつ必須パラメータとなります.ちなみにドキュメントではType Hintsにて引数の型を指定していますが,この記載方法でなくても動作します.
#!/usr/bin/env python # -*- coding: utf-8 -*- import typer def main(name: str): print(f"Hello {name}") if __name__ == "__main__": typer.run(main)
早速実行してみましょう.
main.py
を実行する際に与えた引数が内部で利用できていることが見てとれます.また,上記の実装方法でTyperを利用した場合は引数指定が必須となるため,引数を指定しないで実行した場合はエラーとなります.
# 引数を指定して実行 $ python main.py Japan Hello Japan # 引数を指定せずに実行 $ python main.py Usage: main.py [OPTIONS] NAME Try 'main.py --help' for help. Error: Missing argument 'NAME'.
引数にデフォルト値を設定する場合は以下の様にします.
Argument
クラスを用いてインスタンス化する際にデフォルト値を指定した変数を引数に与えることで実現できます.
#!/usr/bin/env python # -*- coding: utf-8 -*- import typer def main(name: str = typer.Argument("World", help="出力する文字列")): print(f"Hello {name}") if __name__ == "__main__": typer.run(main)
先ほどは引数を与えない場合はエラーとなっていましたが,Argument
にてデフォルト値を指定した場合はエラーにはならずデフォルトで指定した文字列が利用されています.上記例ではデフォルト値として World
が設定されているので Hello World
と出力されていることが見て取れますね.
# 引数を指定して実行 $ python main.py Japan Hello Japan # 引数を指定して実行 $ python main.py Hello World
Augumentには --help
オプションにてコマンドの利用方法を表示するhelpテキストを記載することも可能です.--help
オプションを表示してみると引数のhelpテキストが表示されていることが確認できます.
$ python main.py --help Usage: main.py [OPTIONS] [NAME] Arguments: [NAME] 出力する文字列 [default: World] Options: --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.
CLI Options
続いてオプションを利用する方法について記載します.
オプションと引数の違いですがコマンドのオプションは「プログラムの動作を指定するもの」で引数は「動作の対象を指定するもの」と把握しておきましょう.
実装は先ほどの引数の場合には Augment
クラスを利用してましたが,オプションの場合には Option
クラスを利用するだけです.Augument
と同様にデフォルトのパラメータを指定すること,helpテキストを定義することが可能です.
#!/usr/bin/env python # -*- coding: utf-8 -*- import typer def main( name: str = typer.Argument("World", help="出力する文字列"), status: str = typer.Option("Great", help="状態を表す文字列"), age: int = typer.Option(None, help="年齢を表す数字") ): if age: print(f"Hello {name}! I am {age}") else: print(f"Hello {name}! I am {status}") if __name__ == "__main__": typer.run(main)
上記のコードを実行した結果を確認してみます.
オプションを指定せずに実行した場合はデフォルトのパラメータが利用されるので,条件文の if age
が else
の処理シーケンスに入るので Hello World! I am Great
が出力されます.オプションを指定した場合は age
に 20
が指定され, if age
が True
となるためHello World! I am 20
が出力されています.
# 引数の指定なし $ python main.py Hello World! I am Great # 引数指定なしでオプションあり python main.py --age 20 Hello World! I am 20
併せて オプション のhelpテキストも確認してみましょう.
オプションに status
と age
がちゃんと追加されているのが見れました.
$ python main.py --help Usage: main.py [OPTIONS] [NAME] Arguments: [NAME] 出力する文字列 [default: World] Options: --status TEXT 状態を表す文字列 [default: Great] --age INTEGER 年齢を表す数字 --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.
また,オプションでは Option
クラスの引数にpromtパラメータを指定してあげると,エラーを表示する代わりに不足しているパラメータをコマンドライン上で対話形式で入力することができます.
#!/usr/bin/env python # -*- coding: utf-8 -*- import typer def main( name: str = typer.Argument("World", help="出力する文字列"), status: str = typer.Option("Great", help="状態を表す文字列"), age: int = typer.Option(None, help="年齢を表す数字", prompt="Please tell me your age") ): if age: print(f"Hello {name}! I am {age}") else: print(f"Hello {name}! I am {status}") if __name__ == "__main__": typer.run(main)
自身が作成したCLIアプリケーションを別の人が利用する際に,アプリケーション側で対話形式での入力機能を持っていると入力するべきパラメータが分かりやすいので非常に有効なツールになると思います.
$ python main.py Please tell me your age: 12 Hello World! I am 12
SubCommand
サブコマンドの利用もTyperで実装可能です.
これまでの引数やオプションと違って Typer
クラスを用いて,生成したインスタンスを使ってサブコマンドに利用したい命名と同じ名前で定義したメソッドに対してデコレータを付与することでサブコマンドが利用できます.
以下では preprocess
と train
,postprocess
のサブコマンドを定義しています.
#!/usr/bin/env python # -*- coding: utf-8 -*- import typer app = typer.Typer() @app.command() def preprocess(subcommand: str = typer.Argument('preprocess')): print(f"select command '{subcommand}'") @app.command() def train(subcommand: str = typer.Argument('train')): print(f"select command '{subcommand}'") @app.command() def postprocess(subcommand: str = typer.Argument('postprocess')): print(f"select command '{subcommand}'") if __name__ == "__main__": app()
3つのサブコマンドが定義できて利用できていることを実行して確認します.
サブコマンドとして train
を指定したら select command 'train'
が表示されております.サブコマンドと引数の組み合わせも簡単に実装できてますね.
$ python main.py train select command 'train' $ python main.py train test select command 'test'
まとめ
今回は『CLIアプリケーションのためのTyper』というタイトルで引数やコマンドラインオプションなどを簡易に実装できるツールとしてTyperを実装例とあわせて紹介しました.
機械学習でアドホックに分析する際にはJupyter Notebookを利用するため,あまりCLIアプリケーションと関わりがない人もおられると思います.一方で,分析した後のシステムへのインプリの段階では多分に漏れなくCLIなどのPythonコードを記述することになると思います.その際にTyperを用いることでコマンドラインで学習や推論と行った処理を分けるプログラムを簡単に実装できるのはありがたいですね.今後も積極的に活用して行きたいと思います.