CLIしたい(typer
)
$ pip3 install typer[all]
1# パッケージ/cli.py
2import typer
3
4app = typer.Typer()
5
6@app.command()
7def hello(name: str):
8 print(f"Hello {name}")
9
10
11@app.command()
12def goodbye(name: str, formal: bool = False):
13 if formal:
14 print(f"Goodbye Ms. {name}. Have a good day.")
15 else:
16 print(f"Bye {name}!")
17
18if __name__ == "__main__":
19 app()
Typerを使うと、サブコマンド付きのCLIをすぐに作ることができます。
上記のサンプルは、公式ドキュメントのAn example with two subcommands - Typerにあるコードです。
とりあえずこのサンプルコードをcli.py
のようなファイルにコピペして動かしてみるだけで、使い方がわかると思います。
これまで、CLIを作るときの引数/オプション解析は、定番のPython標準argparse
パッケージを使っていましたが、サブコマンドを作るのはちょっと大変な印象でした。
(やってみようと思って調べたことはありますが実際に作ったことはない・・・)
Typer
は、引数とオプション、コマンドの説明も、いつもの関数を作る作業の延長ででき、非常に簡単だと感じました。
位置引数したい(typer.Argument
)
1# 簡単設定
2name: str # 位置引数(required)
3
4# 詳細設定
5name: Annotated[int, typer.Argument(help="名前")] # 位置引数(required)
6name: Annotated[int, typer.Argument(help="名前")] = 0 # 位置引数(optional)
コマンドの引数に型ヒントを指定します。
引数のヘルプなど詳細設定したい場合はtyping_extensions.Annotated
を利用します。
デフォルトでは関数のdocstring
がコマンドの説明になりますが、
type.Argument
のhelp
で引数ごとにヘルプを追加できます。
簡単設定では required な位置引数のみ設定できます。 詳細設定では optional な位置引数も設定できます。
オプション引数したい(typer.Option
)
1# 簡単設定
2name: int = 0 # オプション引数(optional)
3
4# 詳細設定
5name: Annotated[int, typer.Option(help="名前")] = 0 # オプション引数(optional)
6name: Annotated[int, typer.Option(help="名前")] # オプション引数(required)
デフォルト値を与えると、オプション引数になります。
引数のヘルプなど詳細設定したい場合はtyping_extensions.Annotated
を利用します。
デフォルトでは関数のdocstring
がコマンドの説明になりますが、
type.Option
のhelp
でオプションごとにヘルプを追加できます。
簡単設定では optional なオプション引数のみ設定できます。 詳細設定では required なオプション引数も設定できます。
CLI Arguments / CLI Options
typer.Argument
/ typer.Option
と
デフォルト値のあり / なしを考えると以下の表のような引数名のパターンが考えられます。
メソッド |
デフォルト値なし |
デフォルト値あり |
---|---|---|
|
CLI arguments |
optional CLI arguments |
|
required CLI options |
CLI options |
基本的には、 CLI arguments(必須の位置引数)、 CLI options(オプション引数) のみのコマンドを設計するとよいと思います。
optional CLI argumentsと、 required CLI optionsは、 次のようなコマンドの使い方になります。
// optional な位置引数
$ cmd [NAME]
// required なオプション引数
$ cmd --name 名前
サブコマンドしたい(@app.command
)
1import typer
2
3app = typer.Typer()
4"""appという名前でtyper.Typerオブジェクトを作成"""
5
6@app.command()
7def vth(
8 """
9 コマンドの説明
10 """
11 ch Annotated[int, typer.Argument(help="チャンネル番号")],
12 vth: Annotated[int, typer.argument(help="スレッショルド値")],
13 max_retry: Annotated[int, typer.argument(help="リトライ数")] = 3,
14 load_from: Annotated[str, typer.argument(help="設定ファイル名")] = "daq.toml"
15 ):
16 pass
17
18if __name__ == "__main__":
19 app()
@app.command
デコレーターでサブコマンドを定義できます。
上記のサンプルではapp = typer.Typer()
を作成しているため、デコレーターは@app.command
になります。
app
の部分は任意のオブジェクト名を使用できます。
出力に色をつけたい(from rich import print
)
1import typer
2from rich import print
3
4...省略...
rich
パッケージのprint
を使うと、出力を色付けできます。
色付けの詳細や、その他の表示形式はドキュメントを参照してください。
中断/終了したい(typer.Exit
)
1import typer
2
3@app.command()
4def コマンド名(is_debug: bool = False):
5 if is_debug:
6 logger.error(f"DEBUG mode : {is_debug}")
7 typer.Exit()
Typer
を使うと、引数のバリデーションを柔軟に書くことができます。
引数の値が正しくない場合に終了する場合、typer.Exit
が使えます。
わざわざimport sys
してsys.exit
する必要がないので便利です。
PoetryでCLIしたい
1# pyproject.toml
2
3[tool.poetry.scripts]
4CLI_NAME = "パッケージ.cli:app"
Poetry
を使って自作CLIを作成する場合は、pyproject.toml
の[tool.poetry.scripts]
セクションに記述します。
詳しくは公式ドキュメントのBuilding a package - Typerを参照してください。