オプションの 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()]
ここで、name
がCLI 引数であることを明示的に示しています。これは依然として 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 と同じ意味になり、これらの詳細を覚えておく必要がなくなります。