コンテンツへスキップ

オプションの CLI 引数

前述したように、デフォルトでは

  • CLI オプションオプションです
  • CLI 引数必須です

繰り返しますが、これらはデフォルトでの動作であり、多くの CLI プログラムおよびシステムにおける規約です。

ただし、これを変更できます。

実際には、必須CLI オプションを持つよりも、オプションCLI 引数を持つ方がはるかに一般的です。

その有用性の例として、ls CLI プログラムの動作を見てみましょう。

// If you just type
$ ls

// ls will "list" the files and directories in the current directory
typer  tests  README.md  LICENSE

// But it also receives an optional CLI argument
$ ls ./tests/

// And then ls will list the files and directories inside of that directory from the CLI argument
__init__.py  test_tutorial

代替のCLI 引数宣言

最初のステップで、CLI 引数を追加する方法を確認しました。

import typer


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


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

次に、同じCLI 引数を作成する別の方法を見てみましょう。

import typer
from typing_extensions import Annotated


def main(name: Annotated[str, typer.Argument()]):
    print(f"Hello {name}")


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

情報

Typer はバージョン 0.9.0 で Annotated のサポートを追加し(推奨を開始)、ました。

古いバージョンを使用している場合は、Annotated を使用しようとするとエラーが発生します。

Annotated を使用する前に、Typer のバージョンを少なくとも 0.9.0 にアップグレードしてください。

以前は、この関数パラメータがありました。

name: str

そして、それを Annotated でラップします。

name: Annotated[str]

これらの両方のバージョンは同じことを意味し、Annotated は標準の Python の一部であり、このために存在します。

ただし、Annotated を使用する 2 番目のバージョンでは、Typerで使用できる追加のメタデータを渡すことができます。

name: Annotated[str, typer.Argument()]

ここで、nameCLI 引数であることを明示的に示しています。これは依然として str であり、必須です(デフォルト値はありません)。

そこで行ったことはすべて、以前と同じように、必須CLI 引数を作成することです。

$ python main.py

Usage: main.py [OPTIONS] NAME
Try "main.py --help" for help.

Error: Missing argument 'NAME'.

まだあまり便利ではありませんが、正しく機能します。

そして、必須CLI 引数を宣言できること

name: Annotated[str, typer.Argument()]

...それは全く同じように機能します。

name: str

...後で役立ちます。

オプションのCLI 引数を作成する

さて、最後に、私たちが望んでいたオプションのCLI 引数です。

CLI 引数をオプションにするには、typer.Argument() を使用し、typer.Argument() の最初のパラメータとして異なる「デフォルト」を渡します。たとえば、None です。

from typing import Optional

import typer
from typing_extensions import Annotated


def main(name: Annotated[Optional[str], typer.Argument()] = None):
    if name is None:
        print("Hello World!")
    else:
        print(f"Hello {name}")


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

これで、次のようになりました。

name: Annotated[Optional[str], typer.Argument()] = None

typer.Argument() を使用しているため、Typer はこれがCLI 引数であることを認識します(必須オプションかに関係なく)。

ヒント

Optional を使用すると、エディターは値が None である可能性があることを認識でき、None の場合に壊れる str であると想定して何かを行った場合に警告することができます。

ヘルプを確認する

// First check the help
$ python main.py --help

Usage: main.py [OPTIONS] [NAME]

Arguments:
  [NAME]

Options:
  --help                Show this message and exit.

ヒント

NAME は依然としてCLI 引数であり、「Usage: main.py ...」に表示されています。

また、[NAME] が括弧(「[」と「]」)で囲まれている(以前は NAME のみでした)ことに注目してください。これは、必須ではなく、オプションであることを示すためです。

実行してテストしてみましょう。

// With no CLI argument
$ python main.py

Hello World!

// With one optional CLI argument
$ python main.py Camila

Hello Camila

ヒント

ここで「Camila」がCLI オプションではなくオプションのCLI 引数であることに注意してください。なぜなら「--name Camila」のようなものは使用せず、「Camila」をプログラムに直接渡したからです。

代替(古い)デフォルト値としての typer.Argument()

Typer は、追加のメタデータを持つCLI 引数を宣言するための、別の古い代替構文もサポートしています。

Annotated を使用する代わりに、デフォルト値として typer.Argument() を使用できます。

import typer


def main(name: str = typer.Argument()):
    print(f"Hello {name}")


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

ヒント

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

以前は、name にデフォルト値がなかったため、Python の用語では、Python 関数の必須パラメータでした。

デフォルト値として typer.Argument() を使用すると、Typer は同じことを行い、それを必須CLI 引数にします。

次のように変更しました。

name: str = typer.Argument()

しかし、typer.Argument() が関数のパラメータの「デフォルト値」になったため、「必須ではなくなった」(Python の用語では)ということになります。

何かが必須かどうか、デフォルト値が何であるかを判断するための Python 関数のデフォルト値(またはその欠如)がなくなったため、typer.Argument() は、そのデフォルト値を定義するか、必須にするという同じ目的を果たす最初のパラメータ default を受け取ります。

default 引数に値を渡さないことは、必須としてマークするのと同じです。ただし、typer.Argument(default=...) に渡される default 引数として ... を渡すことで、明示的に必須としてマークすることもできます。

name: str = typer.Argument(default=...)

情報

以前に ... を見たことがない場合:これは特別な単一の値であり、Python の一部であり、「Ellipsis」と呼ばれます

import typer


def main(name: str = typer.Argument(default=...)):
    print(f"Hello {name}")


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

同じように、異なる default 値(たとえば、None)を渡すことで、オプションにすることができます。

from typing import Optional

import typer


def main(name: Optional[str] = typer.Argument(default=None)):
    if name is None:
        print("Hello World!")
    else:
        print(f"Hello {name}")


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

typer.Argument(default=None) に渡される最初のパラメータ(新しい「デフォルト」値)が None であるため、Typer はこれがオプションCLI 引数であることを認識します。コマンドラインで呼び出すときに値が提供されない場合、デフォルト値の None が設定されます。

default 引数は最初の引数であるため、default= を明示的に使用せずに値を渡すコードが表示される可能性があります。

name: str = typer.Argument(...)

...または、このように。

name: str = typer.Argument(None)

...ただし、繰り返しますが、可能な場合は Annotated を使用してみてください。そうすれば、Python の観点から見たコードは Typer と同じ意味になり、これらの詳細を覚えておく必要がなくなります。