Notice that we don't have to get the typer.Context and check for ctx.resilient_parsing for completion to work, because we only print and modify the program when --version is passed, otherwise, nothing is printed or changed from the callback.
If the --versionCLI option is passed, we get a value True in the callback.
Then we can print the version and raise typer.Exit() to make sure the program is terminated before anything else is executed.
We also declare the explicit CLI option name --version, because we don't want an automatic --no-version, it would look awkward.
Check it:
fast βpython main.py --help π¬ We get a --version, and don't get an awkward --no-version πUsage: main.py [OPTIONS]
Options: --version --name TEXT --help Show this message and exit.
π¬ We can call it normallypython main.py --name Camila Hello Camila
π¬ And we can get the versionpython main.py --version Awesome CLI Version: 0.1.0
π¬ Because we exit in the callback, we don't get a "Hello World" message after the version π restart β»
But now let's say that the --nameCLI option that we declared before --version is required, and it has a callback that could exit the program:
fromtypingimportOptionalimporttyperfromtyping_extensionsimportAnnotated__version__="0.1.0"defversion_callback(value:bool):ifvalue:print(f"Awesome CLI Version: {__version__}")raisetyper.Exit()defname_callback(name:str):ifname!="Camila":raisetyper.BadParameter("Only Camila is allowed")defmain(name:Annotated[str,typer.Option(callback=name_callback)],version:Annotated[Optional[bool],typer.Option("--version",callback=version_callback)]=None,):print(f"Hello {name}")if__name__=="__main__":typer.run(main)
Tip
Prefer to use the Annotated version if possible.
fromtypingimportOptionalimporttyper__version__="0.1.0"defversion_callback(value:bool):ifvalue:print(f"Awesome CLI Version: {__version__}")raisetyper.Exit()defname_callback(name:str):ifname!="Camila":raisetyper.BadParameter("Only Camila is allowed")defmain(name:str=typer.Option(...,callback=name_callback),version:Optional[bool]=typer.Option(None,"--version",callback=version_callback),):print(f"Hello {name}")if__name__=="__main__":typer.run(main)
Then our CLI program could not work as expected in some cases as it is right now, because if we use --version after --name then the callback for --name will be processed before and we can get its error:
fast βpython main.py --name Rick --version Only Camila is allowed Aborted!
We don't have to check for ctx.resilient_parsing in the name_callback() for completion to work, because we are not using typer.echo(), instead we are raising a typer.BadParameter.
Technical Details
typer.BadParameter prints the error to "standard error", not to "standard output", and because the completion system only reads from "standard output", it won't break completion.