-
-
Notifications
You must be signed in to change notification settings - Fork 664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEATURE] Add easy --version #52
Comments
This would be great. |
Hey everyone! Thanks for the discussion! Yeah, Click decorators won't work with Typer, although you can generate a Click object from a Typer app (there's a new section in the docs about it). But I just added docs about adding a |
How to use version when I'm using app with different sub commands. Is there any example to see how to add global parameters across all subcommands? |
@kesavkolla yes you just add a callback function for your app and then use the Docs on callback: https://typer.tiangolo.com/tutorial/commands/callback/ Docs on Example combining the two: import typer
app = typer.Typer()
def version_callback(value: bool):
if value:
typer.echo(f"Awesome CLI Version: {__version__}")
raise typer.Exit()
@app.callback()
def main(
version: bool = typer.Option(None, "--version", callback=version_callback, is_eager=True),
):
# Do other global stuff, handle other global options here
return |
@dbanty The example above is incomplete and is missing a critical part to at the end:
|
If you’re running the script directly, yeah you’ll need that. I always include my entrypoints in my Poetry scripts so they’re installable. I often forget how non-poetry users do things 😅. |
In fact I needed a bit longer version to make it work when called as a module because you need another method to be called as entry point, you cannot use the main() from the example. Full solutions added this to the bottom:
With that inside setup.cfg:
What I show above works with all 3 caling methods:
There is one small bit that is not sorted yet and related to flake8 producing |
@ssbarnea the same workaround as suggested for FastAPI works: fastapi/fastapi#1522 (comment) |
would it be even greater if the version value is read from poetry's pyproject.toml file? maybe something like a typer plugin for poetry, so it generates this version CLI option automatically? |
Not to necro the thread, but it took me quite a few tries to get version flags working. Was able to modify @dbanty's post to look like this: import typer
from myapp import __version__
app = typer.Typer()
def version_callback(value: bool):
if value:
print(__version__)
raise typer.Exit()
@app.callback()
def version(
version: bool = typer.Option(None,
"--version", "-v",
callback=version_callback,
is_eager=True,
help="Print the version and exit")
):
pass
if __name__ == "__main__":
app() @shelper FWIW I'm using poetry as well and when I build my tiny app, it's possible to bump the For truly automatic semver, release-please is sweet. |
I agree that I think the original suggestion of automatically adding "--version" to a Typer app would be awesome, e.g.: # auto detect package name and version
app = typer.Typer(version_option=True)
# specify version string manually
app = typer.Typer(version_option="myapp v1.3.5") |
If you want it automatically, you can do it that way: import inspect
import typer
cli = typer.Typer(help="CLI for mypackage", pretty_exceptions_enable=False)
def get_package_name() -> str:
frame = inspect.currentframe()
f_back = frame.f_back if frame is not None else None
f_globals = f_back.f_globals if f_back is not None else None
# break reference cycle
# https://docs.python.org/3/library/inspect.html#the-interpreter-stack
del frame
package_name: str | None = None
if f_globals is not None:
package_name = f_globals.get("__name__")
if package_name == "__main__":
package_name = f_globals.get("__package__")
if package_name:
package_name = package_name.partition(".")[0]
if package_name is None:
raise RuntimeError("Could not determine the package name automatically.")
return package_name
def get_version(*, package_name: str) -> str:
import importlib.metadata
version: str | None = None
try:
version = importlib.metadata.version(package_name)
except importlib.metadata.PackageNotFoundError:
raise RuntimeError(f"{package_name!r} is not installed.") from None
if version is None:
raise RuntimeError(f"Could not determine the version for {package_name!r} automatically.")
return version
def version_callback(*, value: bool):
if value:
package_name = get_package_name()
version = get_version(package_name=package_name)
typer.echo(f"{package_name}, {version}")
raise typer.Exit()
@cli.callback()
def callback(
version: bool = typer.Option(None, "--version", callback=version_callback, is_eager=True),
) -> None:
pass
if __name__ == "__main__":
cli() |
The point is, a "--version" option is something basically every CLI tool should have. This strongly suggests it should be provided by the framework, similary to the "--help" option. Click adds "--version" with one line of code, and you do not need to understand any advanced concepts, or have extensive familiarity with the abstractions of your framework. Just tab complete it in your editor, and it's done. Typer should provide a nice default mechanism for this, at least as good and as easy as Click, rather than forcing everyone to implement it themselves using Typer callbacks... Which, frankly, is a construct that I didn't really understand the first few times I read through the documentation. Some lines from The Zen of Python:
|
Is https://typer.tiangolo.com/tutorial/options/version/ still the way to go? |
I don't like the idea of having an unused parameter import click
from typer import Typer
from typer.core import TyperCommand # or TyperGroup
class MyCommand(TyperCommand):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
click.version_option(prog_name="testapp")(self)
app = Typer() # or cls=MyGroup
@app.command(cls=MyCommand)
def main():
print("Hello!")
if __name__ == "__main__":
app() |
Is your feature request related to a problem
I'd like a way to do the standard
my-command --version
to exit an print the version number of the application. Click offers aversion_option
decorator that you can put on the entrypoint of your application to achieve this relatively easily.The solution you would like
The ability to pass in a version string to an application and have the option added automatically. I'd then end up doing something like this:
Though if it's possible to attempt to extract the package version automatically without having to pass it in, that would be even better.
Describe alternatives you've considered
I attempted to just add a version option in my callback like below, but I can't seem to call this without passing in a command.
However if I just try
my-command --version
it gives me an error saying I need to pass a command. It would be nice to be able to add custom top level options like--help
which are allowed to run. Then users could implement things like--version
themselves.The text was updated successfully, but these errors were encountered: