コンテンツにスキップ

ネストされたサブコマンド

ここでは、これらの同じアイデアが、深くネストされたコマンドにどのように拡張できるかを見ていきます。

前の例と同じCLIプログラムが、landsを処理する必要があると仮定しましょう。

ただし、土地はreign(領地)またはtown(町)のいずれかです。

そして、それぞれがcreatedeleteのような独自のコマンドを持つことができます。

領地のためのCLIアプリ

まず、ファイルreigns.pyから始めましょう。

import typer

app = typer.Typer()


@app.command()
def conquer(name: str):
    print(f"Conquering reign: {name}")


@app.command()
def destroy(name: str):
    print(f"Destroying reign: {name}")


if __name__ == "__main__":
    app()

これは、領地を管理するためのシンプルなCLIプログラムです。

// Check the help
$ python reigns.py --help

Usage: reigns.py [OPTIONS] COMMAND [ARGS]...

Options:
  --install-completion  Install completion for the current shell.
  --show-completion     Show completion for the current shell, to copy it or customize the installation.
  --help                Show this message and exit.

Commands:
  conquer
  destroy

// Try it
$ python reigns.py conquer Cintra

Conquering reign: Cintra

$ python reigns.py destroy Mordor

Destroying reign: Mordor

町のためのCLIアプリ

次に、towns.pyで町を管理するための同等のものを作成します。

import typer

app = typer.Typer()


@app.command()
def found(name: str):
    print(f"Founding town: {name}")


@app.command()
def burn(name: str):
    print(f"Burning town: {name}")


if __name__ == "__main__":
    app()

これにより、町を管理できます。

// Check the help
$ python towns.py --help

Usage: towns.py [OPTIONS] COMMAND [ARGS]...

Options:
  --install-completion  Install completion for the current shell.
  --show-completion     Show completion for the current shell, to copy it or customize the installation.
  --help                Show this message and exit.

Commands:
  burn
  found

// Try it
$ python towns.py found "New Asgard"

Founding town: New Asgard

$ python towns.py burn Vizima

Burning town: Vizima

CLIアプリで土地を管理する

次に、reignstownsを同じCLIプログラム内のlands.pyにまとめましょう。

import typer

import reigns
import towns

app = typer.Typer()
app.add_typer(reigns.app, name="reigns")
app.add_typer(towns.app, name="towns")

if __name__ == "__main__":
    app()

これで、独自のコマンドを持つコマンド(またはコマンドグループ)reignsと、独自のサブコマンドを持つ別のコマンドtownsを持つ単一のCLIプログラムができました。

確認してください

// Check the help
$ python lands.py --help

Usage: lands.py [OPTIONS] COMMAND [ARGS]...

Options:
  --install-completion  Install completion for the current shell.
  --show-completion     Show completion for the current shell, to copy it or customize the installation.
  --help                Show this message and exit.

Commands:
  reigns
  towns

// We still have the help for reigns
$ python lands.py reigns --help

Usage: lands.py reigns [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  conquer
  destroy

// And the help for towns
$ python lands.py towns --help

Usage: lands.py towns [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  burn
  found

試してみて、CLIを通して土地を管理してください。

// Try the reigns command
$ python lands.py reigns conquer Gondor

Conquering reign: Gondor

$ python lands.py reigns destroy Nilfgaard

Destroying reign: Nilfgaard

// Try the towns command
$ python lands.py towns found Springfield

Founding town: Springfield

$ python lands.py towns burn Atlantis

Burning town: Atlantis

深くネストされたサブコマンド

ここで、lands.pyCLIプログラム内のこれらすべてのコマンドが、最初の例で構築した以前のCLIプログラムの一部である必要があるとします。

CLIプログラムに、以下のコマンド/コマンドグループを持たせたいとします。

  • users:
    • create
    • delete
  • items:
    • create
    • delete
    • sell
  • lands:
    • reigns:
      • conquer
      • destroy
    • towns:
      • found
      • burn

これはすでにかなり深くネストされたコマンド/コマンドグループの「ツリー」です。

しかし、これを実現するには、lands Typerアプリを既にあるmain.pyファイルに追加するだけです。

import typer

import items
import lands
import users

app = typer.Typer()
app.add_typer(users.app, name="users")
app.add_typer(items.app, name="items")
app.add_typer(lands.app, name="lands")

if __name__ == "__main__":
    app()

これで、すべてが単一のCLIプログラムにまとまりました。

// Check the main help
$ python main.py --help

Usage: main.py [OPTIONS] COMMAND [ARGS]...

Options:
  --install-completion  Install completion for the current shell.
  --show-completion     Show completion for the current shell, to copy it or customize the installation.
  --help                Show this message and exit.

Commands:
  items
  lands
  users

// Try some users commands
$ python main.py users create Camila

Creating user: Camila

// Now try some items commands
$ python main.py items create Sword

Creating item: Sword

// And now some lands commands for reigns
$ python main.py lands reigns conquer Gondor

Conquering reign: Gondor

// And for towns
$ python main.py lands towns found Cartagena

Founding town: Cartagena

ファイルの確認

確認/コピーしたい場合は、すべてのファイルがここにあります。

reigns.py:

import typer

app = typer.Typer()


@app.command()
def conquer(name: str):
    print(f"Conquering reign: {name}")


@app.command()
def destroy(name: str):
    print(f"Destroying reign: {name}")


if __name__ == "__main__":
    app()

towns.py:

import typer

app = typer.Typer()


@app.command()
def found(name: str):
    print(f"Founding town: {name}")


@app.command()
def burn(name: str):
    print(f"Burning town: {name}")


if __name__ == "__main__":
    app()

lands.py:

import typer

import reigns
import towns

app = typer.Typer()
app.add_typer(reigns.app, name="reigns")
app.add_typer(towns.app, name="towns")

if __name__ == "__main__":
    app()

users.py:

import typer

app = typer.Typer()


@app.command()
def create(user_name: str):
    print(f"Creating user: {user_name}")


@app.command()
def delete(user_name: str):
    print(f"Deleting user: {user_name}")


if __name__ == "__main__":
    app()

items.py:

import typer

app = typer.Typer()


@app.command()
def create(item: str):
    print(f"Creating item: {item}")


@app.command()
def delete(item: str):
    print(f"Deleting item: {item}")


@app.command()
def sell(item: str):
    print(f"Selling item: {item}")


if __name__ == "__main__":
    app()

main.py:

import typer

import items
import lands
import users

app = typer.Typer()
app.add_typer(users.app, name="users")
app.add_typer(items.app, name="items")
app.add_typer(lands.app, name="lands")

if __name__ == "__main__":
    app()

ヒント

これらのファイルはすべて、if __name__ == "__main__"ブロックを持ち、それぞれが独立したCLIアプリとしてどのように機能するかを示しています。

ただし、最終的なアプリケーションに必要なのは、main.pyだけです。

まとめ

以上です。必要に応じて、Typerアプリケーションをいくつでも入れ子にして、簡単なコードを記述しながら複雑なCLIプログラムを作成できます。

おそらく、ここに示す例よりも使いやすい、よりシンプルなCLIプログラムを設計できるでしょう。しかし、要件が複雑な場合、Typerを使用すると、CLIアプリを簡単に構築できます。

ヒント

オートコンプリートは、特に複雑なプログラムでは非常に役立ちます。

CLIアプリにオートコンプリートを追加する方法については、ドキュメントを確認してください。