📌

PythonのコマンドラインパーサーTyper浅煎り

2022/07/31に公開

PythonのコマンドラインパーサーであるTyper記事が、日本語では見つからな日本語の解説記事が少なかったので、メモがてら書いてみました。

Typerは次のような特徴を持つ新進気鋭のコマンドラインパーサーです。

  • 出力が今風でファンシー
  • タイプヒントも利用しつつ、直感的にコマンドラインの仕様を書き下せる
  • コマンドライン補完機能を標準装備

※ 記事の執筆時点、2022.07.31現在ではベータであるため、正式リリースまでには機能の変更があるかもしません。

インストール

python3 -m pip install "typer[all]"

最小限のコマンドライン仕様の定義

使い方は、まず大きく、コマンドが不要な場合と必要な場合に分けることになります。

(1) コマンドが不要な場合

import typer

def main(name: str):
    print(f"Hello {name}")

if __name__ == "__main__":
    typer.run(main)

必須のコマンドライン引数としてnameを取ります。

この定義で、次のように、エラーメッセージやヘルプメッセージの生成も含めたコマンドラインパーサーを利用することができます。

(2) コマンドが必要な場合

import typer

app = typer.Typer()

@app.command()
def hello(name: str):  # ← 関数名がコマンド名になる
    print(f"Hello {name}")

@app.command()
def goodbye(name: str, formal: bool = False):
    if formal:
        print(f"Goodbye Ms. {name}. Have a good day.")
    else:
        print(f"Bye {name}!")

if __name__ == "__main__":
    app()

コマンドラインの引数として、コマンド名helloまたはgoodbyeを要求します。

helloコマンドは必須のコマンドライン引数としてnameを取ります。
goodbypeコマンドは必須のコマンドライン引数としてnameを取り、オプションとして--formalを取ります。

formalがオプションになったのは、関数goodbyeの引数formalが、デフォルト値がある(すなわち、省略可能な)bool型の引数として定義されているためです。

この定義で、コマンドをつけずに--helpとするとコマンド一覧を表示し、コマンドをつけて--helpとするとそのコマンドのコマンドラインの説明を表示するようにできます。

雛形

引数やオプションの指定の仕方、オプションの短い名前、複数個の引数やオプションを含んだ例を示します。
後はこれをコピーして書き換えていってください。

from typing import List, Optional

import typer

app = typer.Typer(help="いろいろな方法で数を表示します。")  # ← ヘルプメッセージの1行目、アプリケーションの説明

@app.command()
def count_up(  # ← アンダースコアはコマンドラインではハイフンになる。このコマンド名はcount-upとなる
    start: int,  # ヘルプメッセージ内で説明なし、必須の引数
    end: int = typer.Argument(..., help="終了値"),  # 説明あり、必須の引数
    step: int = typer.Argument(1, help="ステップ")  # 説明あり、略可能な引数を(typer.Argumentの最初の引数にデフォルト値を渡す)
):

    """
    開始値から終了値までカウントアップします。
    """  # ← コマンドの説明はDocstringで書く

    print(" ".join("%d" % i for i in range(start, end + 1, step)))

@app.command()
def format(
    value: float,
    format_string: str = typer.Option("%g", "--format-string", "-f", help="フォーマット文字列"),  # ← 1文字のオプションを定義する例
):
    """
    与えられた数値をフォーマット文字列によりフォーマットします。
    """

    print(format_string % value)

@app.command()
def count_args(
    args: Optional[List[str]] = typer.Argument(None),  # ← 複数個の値を並べられる引数の例
    exclude: List[str] = typer.Option([], help="これと等しい引数は数えません(除外します)。"),  # ← 複数回指定できるオプションの例
):
    """
    引数の個数を数えます。
    """

    if args is None:
        args = []
    print(len([arg for arg in args if arg not in exclude]))

if __name__ == "__main__":
    app()

コマンドライン補完

上のスクリーンショットでも出ていましたが、Typerにはコマンドライン補完機能をシェルにインストールする--install-completionという機能があります。

ただ、これを実行すると、シェルの設定ファイルを作成するため、以降のログインセッションすべてに影響します。利用はちょっとためらわれるかもしれません。

※ (2022.07.31現在) 公式ドキュメントの記述によると、typer-cliというパッケージを利用することでシェルの設定ファイルを使わずにコマンドライン補完が利用できそうですが、まだ実験段階らしく、PyPIに登録してあるものはtyperとのバージョンの整合性が保てませんでした。typer-cliをインストールすると、typerも古いものがインストールされてしまうため、python3 -m pip uninstall typer-cli typerとして、typerもいったんアンインストールしてください。

その他の機能

Typerには、ここで紹介した以外にも、次のような機能があります。

正常終了する

raise typer.Exit()

異常終了する

raise typer.Exit(code=1)

パスワードを入力させる

password: str = typer.Option(
    ..., prompt=True, confirmation_prompt=True, hide_input=True
),

他の日本語での解説記事

※ 記事を書いた後になってなぜか検索できるようになっていました。参考のためにリンクをつけます。

Discussion