One or Multiple Commands

You might have noticed that if you create a single command, as in the first example:

import typer

app = typer.Typer()


@app.command()
def main(name: str):
    typer.echo(f"Hello {name}")


if __name__ == "__main__":
    app()

Typer is smart enough to create a CLI application with that single function as the main CLI application, not as a command/subcommand:

// Without a CLI argument
$ python main.py

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

Error: Missing argument "NAME".

// With the NAME CLI argument
$ python main.py Camila

Hello Camila

// Asking for help
$ python main.py

Usage: main.py [OPTIONS] NAME

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.

Tip

Notice that it doesn't show a command main, even though the function name is main.

But if you add multiple commands, Typer will create one CLI command for each one of them:

import typer

app = typer.Typer()


@app.command()
def create():
    typer.echo("Creating user: Hiro Hamada")


@app.command()
def delete():
    typer.echo("Deleting user: Hiro Hamada")


if __name__ == "__main__":
    app()

Here we have 2 commands create and delete:

// 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:
  create
  delete

// Test the commands
$ python main.py create

Creating user: Hiro Hamada

$ python main.py delete

Deleting user: Hiro Hamada

One command and one callback

If you want to create a CLI app with one single command but you still want it to be a command/subcommand you can just add a callback:

import typer

app = typer.Typer()


@app.command()
def create():
    typer.echo("Creating user: Hiro Hamada")


@app.callback()
def callback():
    pass


if __name__ == "__main__":
    app()

And now your CLI program will have a single command.

Check it:

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

// Notice the single command create
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:
  create

// Try it
$ python main.py create

Creating user: Hiro Hamada

Using the callback to document

Now that you are using a callback just to have a single command, you might as well use it to add documentation for your app:

import typer

app = typer.Typer()


@app.command()
def create():
    typer.echo("Creating user: Hiro Hamada")


@app.callback()
def callback():
    """
    Creates a single user Hiro Hamada.

    In the next version it will create 5 users more.
    """


if __name__ == "__main__":
    app()

And now the docstring from the callback will be used as the help text:

$ python main.py --help

// Notice the help text from the docstring
Usage: main.py [OPTIONS] COMMAND [ARGS]...

  Creates a single user Hiro Hamada.

  In the next version it will create 5 users more.

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:
  create

// And it still works the same, the callback does nothing
$ python main.py create

Creating user: Hiro Hamada