pythontyper

How can I get a --version to the root of a typer.Typer application?


My CLI applications typically have subcommands. I want to have the --version flag at the root of my CLI applications, but with Typer I've only seen ways to put it to a command. I want to add it to the typer.Typer object (the root) itself. How can I do that?

What I've tried

import typer
from typing import Optional

__version__ = "0.1.0"


def version_callback(value: bool):
    if value:
        typer.echo(f"Awesome CLI Version: {__version__}")
        raise typer.Exit()


app = typer.Typer(
    add_completion=False,
)


@app.command()
def main(
    version: Optional[bool] = typer.Option(
        None, "--version", callback=version_callback
    ),
) -> None:
    pass


@app.command()
def foo() -> None:
    pass


if __name__ == "__main__":
    app()

This gives

$ python cli.py --help   
Usage: cli.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  foo
  main

$ python cli.py main --help
Usage: cli.py main [OPTIONS]

Options:
  --version
  --help     Show this message and exit.

What I wanted:

$ python cli.py --help   
Usage: cli.py [OPTIONS] COMMAND [ARGS]...

Options:
  --version
  --help  Show this message and exit.

Commands:
  foo

Solution

  • This is addressed in the documentation:

    But as those CLI parameters are handled by each of those commands, they don't allow us to create CLI parameters for the main CLI application itself.

    But we can use @app.callback() for that.

    It's very similar to @app.command(), but it declares the CLI parameters for the main CLI application (before the commands):

    To do what you want, you could write something like this:

    import typer
    from typing import Optional
    
    __version__ = "0.1.0"
    
    
    def version_callback(value: bool):
        if value:
            typer.echo(f"Awesome CLI Version: {__version__}")
            raise typer.Exit()
    
    
    app = typer.Typer(
        add_completion=False,
    )
    
    
    @app.callback()
    def common(
        ctx: typer.Context,
        version: bool = typer.Option(None, "--version", callback=version_callback),
    ):
        pass
    
    
    @app.command()
    def main() -> None:
        pass
    
    
    @app.command()
    def foo() -> None:
        pass
    
    
    if __name__ == "__main__":
        app()
    

    Which gives us:

    $ python typertest.py --help
    Usage: typertest.py [OPTIONS] COMMAND [ARGS]...
    
    Options:
      --version
      --help     Show this message and exit.
    
    Commands:
      foo
      main