If you already have a Click application and want to migrate to Typer, or to add some Typer components, you can get a Click Command from your Typer application and then use Click directly.
Any Click application has an object of class Command. That's, more or less, the most basic Click object.
A Command can have its own CLI arguments and CLI options, and it has a function that it calls.
For example, in this Click app:
importclick@click.command()@click.option("--count",default=1,help="Number of greetings.")@click.option("--name",prompt="Your name",help="The person to greet.")defhello(count,name):"""Simple program that greets NAME for a total of COUNT times."""forxinrange(count):click.echo(f"Hello {name}!")if__name__=="__main__":hello()
The original hello variable is converted by Click from a function to a Command object. And the original hello function is used by that Command internally, but it is no longer named hello (as hello is now a Click Command).
Then Click also has a Group class, it inherits from Command. So, a Group object is also a Command.
A Group can also have its own CLI arguments and CLI options.
A Group can have subcommands of class Command or sub groups of class Group as well.
And a Group can also have a function that it calls, right before calling the function for any specific subcommand.
For example:
importclick@click.group()defcli():pass@click.command()definitdb():click.echo("Initialized the database")@click.command()defdropdb():click.echo("Dropped the database")cli.add_command(initdb)cli.add_command(dropdb)if__name__=="__main__":cli()
The cli variable is converted by Click from a function to a Group object. And the original cli function is used by that Group internally.
Tip
The original cli function would be the equivalent of a Typer Callback.
Then the cli variable, that now is a Group object, is used to add sub-commands.
For example, you could have a Typer app, generate a Click Group from it, and then include other Click apps in it:
importclickimporttyperapp=typer.Typer()@app.command()deftop():""" Top level command, form Typer """print("The Typer app is at the top level")@app.callback()defcallback():""" Typer app, including Click subapp """@click.command()@click.option("--name",prompt="Your name",help="The person to greet.")defhello(name):"""Simple program that greets NAME for a total of COUNT times."""click.echo(f"Hello {name}!")typer_click_object=typer.main.get_command(app)typer_click_object.add_command(hello,"hello")if__name__=="__main__":typer_click_object()
Notice that we add a callback that does nothing (only document the CLI program), to make sure Typer creates a Click Group. That way we can add sub-commands to that Click Group.
Then we generate a Click object from our typer.Typer app (typer_click_object), and then we can include another Click object (hello) in this Click Group.
And that way, our Typer app will have a subcommand top built with Typer, and a subcommand hello built with Click.
Check it:
fast →python main.py --help 💬 Notice we have both subcommands, top and helloUsage: 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: hello top
💬 Call the Typer partpython main.py top The Typer app is at the top level
The same way, you can do the contrary and include a Typer sub app in a bigger Click app:
importclickimporttyper@click.group()defcli():pass@cli.command()definitdb():click.echo("Initialized the database")@cli.command()defdropdb():click.echo("Dropped the database")app=typer.Typer()@app.command()defsub():""" A single-command Typer sub app """print("Typer is now below Click, the Click app is the top level")typer_click_object=typer.main.get_command(app)cli.add_command(typer_click_object,"sub")if__name__=="__main__":cli()
Notice that we don't have to add a callback or more commands, we can just create a Typer app that generates a single Click Command, as we don't need to include anything under the Typer app.
Then we generate a Click object from our typer.Typer app (typer_click_object), and then we use the Click cli to include our Click object from our Typer app.
In this case, the original Click app includes the Typer app.
And then we call the original Click app, not the Typer app.
Check it:
fast →python main.py 💬 We get our Typer app down there in the sub commandUsage: main.py [OPTIONS] COMMAND [ARGS]...
Options: --help Show this message and exit.
Commands: dropdb initdb sub A single-command Typer sub app
💬 Use the Click partpython main.py initdb Initialized the database
💬 And use the Typer partpython main.py sub Typer is now below Click, the Click app is the top level
But if you need to use something based on Click decorators, you can always generate a Click object using the methods described above, and use it as you would normally use Click.