Skip to content

SubCommands in a Single File

In some cases, it's possible that your application code needs to live on a single file.

You can still use the same ideas:

import typer

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


@items_app.command("create")
def items_create(item: str):
    typer.echo(f"Creating item: {item}")


@items_app.command("delete")
def items_delete(item: str):
    typer.echo(f"Deleting item: {item}")


@items_app.command("sell")
def items_sell(item: str):
    typer.echo(f"Selling item: {item}")


@users_app.command("create")
def users_create(user_name: str):
    typer.echo(f"Creating user: {user_name}")


@users_app.command("delete")
def users_delete(user_name: str):
    typer.echo(f"Deleting user: {user_name}")


if __name__ == "__main__":
    app()

There are several things to notice here...

Apps at the top

First, you can create typer.Typer() objects and add them to another one at the top.

It doesn't have to be done after creating the subcommands:

import typer

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


@items_app.command("create")
def items_create(item: str):
    typer.echo(f"Creating item: {item}")


@items_app.command("delete")
def items_delete(item: str):
    typer.echo(f"Deleting item: {item}")


@items_app.command("sell")
def items_sell(item: str):
    typer.echo(f"Selling item: {item}")


@users_app.command("create")
def users_create(user_name: str):
    typer.echo(f"Creating user: {user_name}")


@users_app.command("delete")
def users_delete(user_name: str):
    typer.echo(f"Deleting user: {user_name}")


if __name__ == "__main__":
    app()

You can add the commands (subcommands) to each typer.Typer() app later and it will still work.

Function names

As you now have subcommands like create for users and for items, you can no longer call the functions with just the name, like def create(), because they would overwrite each other.

So we use longer names:

import typer

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


@items_app.command("create")
def items_create(item: str):
    typer.echo(f"Creating item: {item}")


@items_app.command("delete")
def items_delete(item: str):
    typer.echo(f"Deleting item: {item}")


@items_app.command("sell")
def items_sell(item: str):
    typer.echo(f"Selling item: {item}")


@users_app.command("create")
def users_create(user_name: str):
    typer.echo(f"Creating user: {user_name}")


@users_app.command("delete")
def users_delete(user_name: str):
    typer.echo(f"Deleting user: {user_name}")


if __name__ == "__main__":
    app()

Command name

We are naming the functions with longer names so that they don't overwrite each other.

But we still want the subcommands to be create, delete, etc.

To call them like:

// We want this ✔️
$ python main.py items create

instead of:

// We don't want this ⛔️
$ python main.py items items-create

So we pass the name we want to use for each subcommand as the function argument to the decorator:

import typer

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


@items_app.command("create")
def items_create(item: str):
    typer.echo(f"Creating item: {item}")


@items_app.command("delete")
def items_delete(item: str):
    typer.echo(f"Deleting item: {item}")


@items_app.command("sell")
def items_sell(item: str):
    typer.echo(f"Selling item: {item}")


@users_app.command("create")
def users_create(user_name: str):
    typer.echo(f"Creating user: {user_name}")


@users_app.command("delete")
def users_delete(user_name: str):
    typer.echo(f"Deleting user: {user_name}")


if __name__ == "__main__":
    app()

Check it

It still works the same:

// Check the 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
  users

Check the items command:

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

// It shows its own commands (subcommands): create, delete, sell
Usage: main.py items [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  create
  delete
  sell

// Try it
$ python main.py items create Wand

Creating item: Wand

$ python main.py items sell Vase

Selling item: Vase

And the same for the users command:

$ python main.py users --help

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

Options:
  --help  Show this message and exit.

Commands:
  create
  delete

// Try it
$ python main.py users create Camila

Creating user: Camila