コンテンツにスキップ

バージョン CLI オプション、is_eager

コールバックを使用して、--version *CLI オプション*を実装できます。

CLI プログラムのバージョンを表示し、他の *CLI パラメーター* が処理される前にプログラムを終了します。

--version の最初のバージョン

どのように見えるか、最初のバージョンを見てみましょう

from typing import Optional

import typer
from typing_extensions import Annotated

__version__ = "0.1.0"


def version_callback(value: bool):
    if value:
        print(f"Awesome CLI Version: {__version__}")
        raise typer.Exit()


def main(
    name: Annotated[str, typer.Option()] = "World",
    version: Annotated[
        Optional[bool], typer.Option("--version", callback=version_callback)
    ] = None,
):
    print(f"Hello {name}")


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

ヒント

可能な場合は、*注釈付き*バージョンを使用することをお勧めします。

from typing import Optional

import typer

__version__ = "0.1.0"


def version_callback(value: bool):
    if value:
        print(f"Awesome CLI Version: {__version__}")
        raise typer.Exit()


def main(
    name: str = typer.Option("World"),
    version: Optional[bool] = typer.Option(
        None, "--version", callback=version_callback
    ),
):
    print(f"Hello {name}")


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

ヒント

補完を機能させるために `typer.Context` を取得して `ctx.resilient_parsing` をチェックする必要はありません。`--version` が渡された場合にのみ出力してプログラムを変更するため、コールバックから何も出力または変更されません。

--version *CLI オプション* が渡されると、コールバックで値 `True` が取得されます。

次に、バージョンを出力し、`typer.Exit()` を発生させて、他の何かが実行される前にプログラムが確実に終了するようにします。

自動的に `--no-version` を使用したくないため、明示的な *CLI オプション* 名 `--version` を宣言します。これは不自然に見えます。

確認してください

$ python main.py --help

// We get a --version, and don't get an awkward --no-version 🎉
Usage: main.py [OPTIONS]

Options:
  --version
  --name TEXT
  --help                Show this message and exit.


// We can call it normally
$ python main.py --name Camila

Hello Camila

// And we can get the version
$ python main.py --version

Awesome CLI Version: 0.1.0

// Because we exit in the callback, we don't get a "Hello World" message after the version 🚀

以前のパラメーターと is_eager

しかし、`--version` の前に宣言した `--name` *CLI オプション* が必須であり、プログラムを終了できるコールバックがあるとしましょう

from typing import Optional

import typer
from typing_extensions import Annotated

__version__ = "0.1.0"


def version_callback(value: bool):
    if value:
        print(f"Awesome CLI Version: {__version__}")
        raise typer.Exit()


def name_callback(name: str):
    if name != "Camila":
        raise typer.BadParameter("Only Camila is allowed")


def main(
    name: Annotated[str, typer.Option(callback=name_callback)],
    version: Annotated[
        Optional[bool], typer.Option("--version", callback=version_callback)
    ] = None,
):
    print(f"Hello {name}")


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

ヒント

可能な場合は、*注釈付き*バージョンを使用することをお勧めします。

from typing import Optional

import typer

__version__ = "0.1.0"


def version_callback(value: bool):
    if value:
        print(f"Awesome CLI Version: {__version__}")
        raise typer.Exit()


def name_callback(name: str):
    if name != "Camila":
        raise typer.BadParameter("Only Camila is allowed")


def main(
    name: str = typer.Option(..., callback=name_callback),
    version: Optional[bool] = typer.Option(
        None, "--version", callback=version_callback
    ),
):
    print(f"Hello {name}")


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

`--name` のコールバックが先に処理され、エラーが発生する可能性があるため、`--version` を `--name` の後に使用すると、CLI プログラムが *現在* のように期待どおりに動作しない場合があります。

$ python main.py --name Rick --version

Only Camila is allowed
Aborted!

ヒント

`typer.echo()` を使用しておらず、代わりに `typer.BadParameter` を発生させているため、補完を機能させるために `name_callback()` で `ctx.resilient_parsing` をチェックする必要はありません。

技術的な詳細

`typer.BadParameter` はエラーを「標準エラー」に出力し、「標準出力」には出力しません。補完システムは「標準出力」からのみ読み取るため、補完は中断されません。

情報

「標準出力」と「標準エラー」とは何かを復習する必要がある場合は、出力と色:「標準出力」と「標準エラー」のセクションを確認してください。

is_eager を使用した修正

このような場合、*CLI パラメーター*(*CLI オプション* または *CLI 引数*)を `is_eager=True` でマークできます。

これは **Typer** (実際には Click) に、この *CLI パラメーター* を他のパラメーターの前に処理する必要があることを伝えます

from typing import Optional

import typer
from typing_extensions import Annotated

__version__ = "0.1.0"


def version_callback(value: bool):
    if value:
        print(f"Awesome CLI Version: {__version__}")
        raise typer.Exit()


def name_callback(name: str):
    if name != "Camila":
        raise typer.BadParameter("Only Camila is allowed")
    return name


def main(
    name: Annotated[str, typer.Option(callback=name_callback)],
    version: Annotated[
        Optional[bool],
        typer.Option("--version", callback=version_callback, is_eager=True),
    ] = None,
):
    print(f"Hello {name}")


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

ヒント

可能な場合は、*注釈付き*バージョンを使用することをお勧めします。

from typing import Optional

import typer

__version__ = "0.1.0"


def version_callback(value: bool):
    if value:
        print(f"Awesome CLI Version: {__version__}")
        raise typer.Exit()


def name_callback(name: str):
    if name != "Camila":
        raise typer.BadParameter("Only Camila is allowed")
    return name


def main(
    name: str = typer.Option(..., callback=name_callback),
    version: Optional[bool] = typer.Option(
        None, "--version", callback=version_callback, is_eager=True
    ),
):
    print(f"Hello {name}")


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

確認してください

$ python main.py --name Rick --version

// Now we only get the version, and the name is not used
Awesome CLI Version: 0.1.0