最初のステップ
最も簡単な例¶
最も簡単な**Typer**ファイルは次のようになります。
import typer
def main():
print("Hello World")
if __name__ == "__main__":
typer.run(main)
これを`main.py`というファイルにコピーしてください。
テストしてみましょう
$ python main.py
Hello World
// It just prints "Hello World".
// Now check the --help
$ python main.py --help
<b> </b><font color="#F4BF75"><b>Usage: </b></font><b>main.py [OPTIONS] </b>
<b> </b>
<font color="#A5A5A1">╭─ Options ─────────────────────────────────────────╮</font>
<font color="#A5A5A1">│ </font><font color="#A1EFE4"><b>--help</b></font> Show this message │
<font color="#A5A5A1">│ and exit. │</font>
<font color="#A5A5A1">╰───────────────────────────────────────────────────╯</font>
...しかし、このプログラムはまだあまり役に立ちません。拡張してみましょう。
**CLI引数**とは¶
ここでは、**CLI引数**という言葉を使って、CLIアプリケーションに特定の順序で渡される**CLIパラメータ**を指します。デフォルトでは、これらは_必須_です。
ターミナルで次のように入力した場合
$ ls ./myproject
first-steps.md intro.md
``ls`は`./myproject`ディレクトリの内容を表示します。
- `ls`は_プログラム_(または「コマンド」、「CLIアプリ」)です。
- `./myproject`は_CLI引数_で、この場合はディレクトリのパスを指します。
これらは、後で説明する**CLIオプション**とは少し異なります。
CLI引数の追加¶
前の例を`name`という引数で更新します
import typer
def main(name: str):
print(f"Hello {name}")
if __name__ == "__main__":
typer.run(main)
$ python main.py
// If you run it without the argument, it shows a nice error
<font color="#F4BF75">Usage: </font>main.py [OPTIONS] NAME
<font color="#A5A5A1">Try </font><font color="#44919F">'main.py </font><font color="#44919F"><b>--help</b></font><font color="#44919F">'</font><font color="#A5A5A1"> for help.</font>
<font color="#F92672">╭─ Error ───────────────────────────────────────────╮</font>
<font color="#F92672">│</font> Missing argument 'NAME'. <font color="#F92672">│</font>
<font color="#F92672">╰───────────────────────────────────────────────────╯</font>
// Now pass that NAME CLI argument
$ python main.py Camila
Hello Camila
// Here "Camila" is the CLI argument
// To pass a name with spaces for the same CLI argument, use quotes
$ python main.py "Camila Gutiérrez"
Hello Camila Gutiérrez
ヒント
スペースを含む単一の値を_CLI引数_に渡す必要がある場合は、その値を引用符(`"`)で囲みます。
2つのCLI引数¶
今度は、名前と名字を分けて入力したいとしましょう。
そこで、`name`と`lastname`の2つの引数を持つように拡張します
import typer
def main(name: str, lastname: str):
print(f"Hello {name} {lastname}")
if __name__ == "__main__":
typer.run(main)
// Check the main --help
$ python main.py --help
<font color="#F4BF75">Usage: </font>main.py [OPTIONS] NAME
<font color="#A5A5A1">Try </font><font color="#44919F">'main.py </font><font color="#44919F"><b>--help</b></font><font color="#44919F">'</font><font color="#A5A5A1"> for help.</font>
<font color="#F92672">╭─ Error ───────────────────────────────────────────╮</font>
<font color="#F92672">│</font> Missing argument 'NAME'. <font color="#F92672">│</font>
<font color="#F92672">╰───────────────────────────────────────────────────╯</font>
<font color="#A1EFE4"><b>typer</b></font> on <font color="#AE81FF"><b> richify</b></font> <font color="#F92672"><b>[»!?] </b></font>via <font color="#F4BF75"><b>🐍 v3.7.5 (env3.7)</b></font>
<font color="#F92672"><b>❯</b></font> <font color="#A6E22E">python</font> <u style="text-decoration-style:single">main.py</u>
<font color="#F4BF75">Usage: </font>main.py [OPTIONS] NAME LASTNAME
<font color="#A5A5A1">Try </font><font color="#44919F">'main.py </font><font color="#44919F"><b>--help</b></font><font color="#44919F">'</font><font color="#A5A5A1"> for help.</font>
<font color="#F92672">╭─ Error ───────────────────────────────────────────╮</font>
<font color="#F92672">│</font> Missing argument 'NAME'. <font color="#F92672">│</font>
<font color="#F92672">╰───────────────────────────────────────────────────╯</font>
// There are now 2 CLI arguments, name and lastname
// Now pass a single name argument
$ python main.py Camila
<font color="#F4BF75">Usage: </font>main.py [OPTIONS] NAME LASTNAME
<font color="#A5A5A1">Try </font><font color="#44919F">'main.py </font><font color="#44919F"><b>--help</b></font><font color="#44919F">'</font><font color="#A5A5A1"> for help.</font>
<font color="#F92672">╭─ Error ───────────────────────────────────────────╮</font>
<font color="#F92672">│</font> Missing argument 'LASTNAME'. <font color="#F92672">│</font>
<font color="#F92672">╰───────────────────────────────────────────────────╯</font>
// These 2 arguments are required, so, pass both:
$ python main.py Camila Gutiérrez
Hello Camila Gutiérrez
ヒント
順序が重要であることに注意してください。名字は名前の後に入力する必要があります。
次のように呼び出した場合
$ python main.py Gutiérrez Camila
アプリケーションはどれが`name`でどれが`lastname`なのかを知る方法がありません。最初の_CLI引数_が`name`で、2番目の_CLI引数_が`lastname`であると想定しています。
**CLIオプション**とは¶
ここでは、**CLIオプション**という言葉を使って、特定の名前でCLIアプリケーションに渡される_CLIパラメータ_を指します。たとえば、ターミナルで次のように入力した場合
$ ls ./myproject --size
12 first-steps.md 4 intro.md
``ls`は`./myproject`ディレクトリの内容を、それぞれの`size`(サイズ)と共に表示します。
- `ls`は_プログラム_(または「コマンド」、「CLIアプリ」)です。
- `./myproject`は_CLI引数_です。
- `--size`はオプションの_CLIオプション_です。
プログラムは、順序ではなく`--size`が表示されているため、サイズを表示する必要があることを認識します。
`--size`のような_CLIオプション_は、_CLI引数_のように順序に依存しません。
そのため、`--size` _を_CLI引数_の前に置いても、機能します(実際、これが最も一般的な方法です)。
$ ls --size ./myproject
12 first-steps.md 4 intro.md
_CLIオプション_と_CLI引数_の主な視覚的な違いは、_CLIオプション_には`--`が名前の前に付加されていることです(例:「`--size`」)。
_CLIオプション_は、事前に定義された名前(ここでは`--size`)を持っているため、順序に依存しません。これは、CLIアプリが「`--size`」という特定の「名前」を持つ、リテラル`--size`パラメータ(「フラグ」または「スイッチ」とも呼ばれます)を具体的に探しているためです。CLIアプリは、入力したかどうかにかかわらず、`--size`があるかどうかを確認するためにアクティブに探します。
対照的に、CLIアプリは「`./myproject`」というテキストを持つ_CLI引数_をアクティブに探しているわけではなく、`./myproject`を入力するか、`./my-super-awesome-project`を入力するか、またはその他の何かを入力するかは知る由もありません。単に、あなたが与えるものを何でも受け取るのを待っているだけです。特定の_CLI引数_を参照していることを知る唯一の方法は、順序によるものです。最初の_CLI引数_が`name`で、2番目が`lastname`であったことを知る方法と同じですが、順序が混在している場合は処理できません。
一方、_CLIオプション_では、順序は問題になりません。
また、デフォルトでは、_CLIオプション_は_オプション_です(_必須_ではありません)。
つまり、デフォルトでは
- _CLI引数_は**必須**です
- _CLIオプション_は**オプション**です
ただし、_必須_と_オプション_のデフォルトは変更できます。
つまり、主な、そして**最も重要な**違いは次のとおりです。
- _CLIオプション_は**`--`で始まる**ため、順序に依存しません
- _CLI引数_は**シーケンスの順序**に依存します
ヒント
上記の例では、_CLIオプション_ `--size`は、コマンドに追加されたかどうかによって、ブール値`True`または`False`を含む単なる「フラグ」または「スイッチ」です。
これは値を受け取りません。しかし、_CLIオプション_は_CLI引数_と同様に値を受け取ることもできます。後ほどその方法を紹介します。
_CLIオプション_の追加¶
それでは、`--formal` _CLIオプション_を追加してみましょう
import typer
def main(name: str, lastname: str, formal: bool = False):
if formal:
print(f"Good day Ms. {name} {lastname}.")
else:
print(f"Hello {name} {lastname}")
if __name__ == "__main__":
typer.run(main)
ここでは、`formal`はデフォルトで`False`の`bool`です。
// Get the help
$ python main.py --help
<b> </b><font color="#F4BF75"><b>Usage: </b></font><b>main.py [OPTIONS] NAME LASTNAME </b>
<b> </b>
<font color="#A5A5A1">╭─ Arguments ─────────────────────────────────────────────────────╮</font>
<font color="#A5A5A1">│ </font><font color="#F92672">*</font> name <font color="#F4BF75"><b>TEXT</b></font> [default: None] <font color="#A6194C">[required]</font> │
<font color="#A5A5A1">│ </font><font color="#F92672">*</font> lastname <font color="#F4BF75"><b>TEXT</b></font> [default: None] <font color="#A6194C">[required]</font> │
<font color="#A5A5A1">╰─────────────────────────────────────────────────────────────────╯</font>
<font color="#A5A5A1">╭─ Options ───────────────────────────────────────────────────────╮</font>
<font color="#A5A5A1">│ </font><font color="#A1EFE4"><b>--formal</b></font> <font color="#AE81FF"><b>--no-formal</b></font> [default: no-formal] │
<font color="#A5A5A1">│ </font><font color="#A1EFE4"><b>--help</b></font> Show this message and │
<font color="#A5A5A1">│ exit. │</font>
<font color="#A5A5A1">╰─────────────────────────────────────────────────────────────────╯</font>
ヒント
`formal`が`bool`であることが検出されたため、自動的に`--formal`と`--no-formal`が作成されることに注意してください。
それでは、通常通りに呼び出してみましょう
$ python main.py Camila Gutiérrez
Hello Camila Gutiérrez
// But if you pass --formal
$ python main.py Camila Gutiérrez --formal
Good day Ms. Camila Gutiérrez.
// And as --formal is a CLI option you can put it anywhere in this command
$ python main.py Camila --formal Gutiérrez
Good day Ms. Camila Gutiérrez.
$ python main.py --formal Camila Gutiérrez
Good day Ms. Camila Gutiérrez.
値を持つ_CLIオプション_¶
`lastname`を_CLI引数_から_CLIオプション_に変換するには、デフォルト値として`""`を指定します
import typer
def main(name: str, lastname: str = "", formal: bool = False):
if formal:
print(f"Good day Ms. {name} {lastname}.")
else:
print(f"Hello {name} {lastname}")
if __name__ == "__main__":
typer.run(main)
`lastname`のデフォルト値が`""`(空の文字列)になったため、関数では必須ではなくなり、**Typer**はデフォルトでこれをオプションの_CLIオプション_にします。
$ python main.py --help
<b> </b><font color="#F4BF75"><b>Usage: </b></font><b>main.py [OPTIONS] NAME </b>
<b> </b>
<font color="#A5A5A1">╭─ Arguments ───────────────────────────────────────────────────────╮</font>
<font color="#A5A5A1">│ </font><font color="#F92672">*</font> name <font color="#F4BF75"><b>TEXT</b></font> [default: None] <font color="#A6194C">[required]</font> │
<font color="#A5A5A1">╰───────────────────────────────────────────────────────────────────╯</font>
<font color="#A5A5A1">╭─ Options ─────────────────────────────────────────────────────────╮</font>
<font color="#A5A5A1">│ </font><font color="#A1EFE4"><b>--lastname</b></font> <font color="#F4BF75"><b>TEXT</b></font> │
<font color="#A5A5A1">│ </font><font color="#A1EFE4"><b>--formal</b></font> <font color="#AE81FF"><b>--no-formal</b></font> <font color="#F4BF75"><b> </b></font> [default: no-formal] │
<font color="#A5A5A1">│ </font><font color="#A1EFE4"><b>--help</b></font> <font color="#F4BF75"><b> </b></font> Show this message │
<font color="#A5A5A1">│ and exit. │</font>
<font color="#A5A5A1">╰───────────────────────────────────────────────────────────────────╯</font>
ヒント
`--lastname`に注目してください。テキスト値を受け取ることがわかります。
`--lastname`のような値を持つ_CLIオプション_(値を持たない_CLIオプション_、`--formal`や`--size`のような`bool`フラグとは対照的)は、_CLIオプション_の_右側_にあるものを何でも値として取ります。
// Call it without a --lastname
$ python main.py Camila
Hello Camila
// Pass the --lastname
$ python main.py Camila --lastname Gutiérrez
Hello Camila Gutiérrez
ヒント
「`Gutiérrez`」が`--lastname`の右側にあることに注意してください。値を持つ_CLIオプション_は、_右側_にあるものを何でも値として取ります。
また、`--lastname`は順序に依存しない_CLIオプション_になったため、名前の前に渡すことができます
$ python main.py --lastname Gutiérrez Camila
// and it will still work normally
Hello Camila Gutiérrez
CLIアプリケーションのドキュメント化¶
関数にdocstringを追加すると、ヘルプテキストで使用されます
import typer
def main(name: str, lastname: str = "", formal: bool = False):
"""
Say hi to NAME, optionally with a --lastname.
If --formal is used, say hi very formally.
"""
if formal:
print(f"Good day Ms. {name} {lastname}.")
else:
print(f"Hello {name} {lastname}")
if __name__ == "__main__":
typer.run(main)
それでは、`--help`オプションで確認してみましょう
$ python main.py --help
<b> </b><font color="#F4BF75"><b>Usage: </b></font><b>main.py [OPTIONS] NAME </b>
<b> </b>
Say hi to NAME, optionally with a <font color="#A1EFE4"><b>--lastname</b></font>.
If <font color="#6B9F98"><b>--formal</b></font><font color="#A5A5A1"> is used, say hi very formally. </font>
<font color="#A5A5A1">╭─ Arguments ───────────────────────────────────────────────────────╮</font>
<font color="#A5A5A1">│ </font><font color="#F92672">*</font> name <font color="#F4BF75"><b>TEXT</b></font> [default: None] <font color="#A6194C">[required]</font> │
<font color="#A5A5A1">╰───────────────────────────────────────────────────────────────────╯</font>
<font color="#A5A5A1">╭─ Options ─────────────────────────────────────────────────────────╮</font>
<font color="#A5A5A1">│ </font><font color="#A1EFE4"><b>--lastname</b></font> <font color="#F4BF75"><b>TEXT</b></font> │
<font color="#A5A5A1">│ </font><font color="#A1EFE4"><b>--formal</b></font> <font color="#AE81FF"><b>--no-formal</b></font> <font color="#F4BF75"><b> </b></font> [default: no-formal] │
<font color="#A5A5A1">│ </font><font color="#A1EFE4"><b>--help</b></font> <font color="#F4BF75"><b> </b></font> Show this message │
<font color="#A5A5A1">│ and exit. │</font>
<font color="#A5A5A1">╰───────────────────────────────────────────────────────────────────╯</font>
ヒント
`--install-completion`や`--help`のように、ヘルプテキストで_CLIオプション_や_CLI引数_の横に表示される具体的なドキュメントを記述する場所が別にあります。チュートリアルの後半で学習します。
引数、オプション、パラメータ、任意、必須¶
これらの用語は、コンテキストによって複数のものを指すことに注意してください。残念ながら、これらの「コンテキスト」は頻繁に混在するため、混乱しやすいです。
Pythonの場合¶
Pythonでは、`name`や`lastname`のような関数の変数の名前は
def main(name: str, lastname: str = ""):
pass
「Python関数パラメータ」または「Python関数引数」と呼ばれます。
技術的な詳細
実際には、Pythonでは「パラメータ」と「引数」の間に非常に小さな違いがあります。
これは非常に技術的な...そしてやや衒学的なものです。
_パラメータ_は、関数の_宣言_における変数名を指します。例:
def bring_person(name: str, lastname: str = ""):
pass
_引数_は、関数を_呼び出す_ときに渡される値を指します。例:
person = bring_person("Camila", lastname="Gutiérrez")
...しかし、ほとんどの場所(ここも含む)で同じ意味で使用されているのを見るでしょう。
Pythonのデフォルト値¶
Pythonでは、関数において、次のような`lastname`のような_デフォルト値_を持つパラメータは
def main(name: str, lastname: str = ""):
pass
は「オプションパラメータ」(または「オプション引数」)とみなされます。
デフォルト値は""
やNone
など、何でも構いません。
そして、name
のようにデフォルト値を持たないパラメータは、*必須*とみなされます。
CLIにおいて¶
コマンドラインインターフェースアプリケーションについて話すとき、**「引数」**と**「パラメータ」**という言葉は、CLIアプリケーションに渡されるデータ、つまりそれらのパラメータを指すために一般的に使用されます。
しかし、これらの言葉は、データが必須であること、特定の順序で渡される必要があること、または--lastname
のようなフラグを持つことについて、*何も暗示していません*。
--lastname
のような名前(とオプションで値)を持つパラメータは、一般的に必須ではなく、オプションです。そのため、CLIについて話すときは、それらを**オプション引数**または**オプションパラメータ**と呼ぶのが一般的です。--
で始まるこれらの*オプションパラメータ*は、**フラグ**または**スイッチ**と呼ばれることもあります。
実際には、順序を必要とするパラメータも*オプション*にすることができます。そして、フラグ(--lastname
など)が付いたパラメータも*必須*にすることができます。
**Typer**において¶
少し分かりやすくするために、通常はPython関数を参照するために「パラメータ」または「引数」という言葉を使用します。
特定の順序に依存する*CLIパラメータ*、つまりデフォルトで**必須**であるものを指すために、**_CLI引数_**を使用します。
そして、--
(--lastname
など)で始まる名前に依存する*CLIパラメータ*、つまりデフォルトで**オプション**であるものを指すために、**_CLIオプション_**を使用します。
*CLI引数*と*CLIオプション*の両方を指すために、**_CLIパラメータ_**を使用します。
typer
コマンド¶
typer
をインストールすると、デフォルトでシェルにtyper
コマンドが追加されます。
このtyper
コマンドを使用すると、ターミナルで✨自動補完✨を使用してスクリプトを実行できます。
Pythonで実行する代わりに
$ python main.py
Hello World
typer
コマンドで実行できます
$ typer main.py run
Hello World
...そして、すべてのコードに対してTABキーを押すと、ターミナルで自動補完が行われます。
そのため、チュートリアルを進めながら、独自のスクリプトの自動補完に使用できます。
ヒント
**Typer**で構築されたCLIアプリケーションは、Pythonパッケージを作成したら、自動補完にtyper
コマンドを必要としません。
しかし、短いスクリプトや学習のためには、Pythonパッケージを作成する前に役立つかもしれません。