Description
First check
- I used the GitHub search to find a similar issue and didn't find it.
- I searched the Typer documentation, with the integrated search.
- I already searched in Google "How to X in Typer" and didn't find any information.
- I already searched in Google "How to X in Click" and didn't find any information.
Description
I'm not sure how to articulate this question without an example. Because of that I had some trouble googling around.
I have two installed packages, foo
and bar
.
bar
can be used independently offoo
. Ex:bar PATH
#bar
does stuff withPATH
foo
acceptsbar
as a plugin. Nowfoo
can use the logic frombar
. Ex:foo bar PATH
. #foo
doesbar
stuff withPATH
foo
can also do other stuff that is built into itself. Ex:foo fizz PATH
. #foo
doesfizz
stuff withPATH
- I have this working with
argparse
but can't getfoo bar PATH
working quite right withtyper
So here goes with an example.
In one file bar/src/bar/cli.py
I have
import typer
app = typer.Typer()
@app.command()
def main(path: str = typer.Argument(...)):
typer.echo(f"Doing bar things to {path}")
If I call bar --help
I get
Usage: bar [OPTIONS] PATH
Options:
--install-completion [bash|zsh|fish|powershell|pwsh]
Install completion for the specified shell.
--show-completion [bash|zsh|fish|powershell|pwsh]
Show completion for the specified shell, to
copy it or customize the installation.
--help Show this message and exit.
which is great.
This is installed with this setup.py
from bar/setup.py
"""Setup."""
from setuptools import find_packages
from setuptools import setup
PACKAGE_NAME = "bar"
setup(
name=PACKAGE_NAME,
version="0.0.1",
package_dir={"": "src"},
packages=find_packages(where="src"),
zip_safe=False,
include_package_data=True,
python_requires=">=3.7",
entry_points={
"console_scripts": [f"{PACKAGE_NAME} = {PACKAGE_NAME}.cli:app"],
"foo.cli_plugins": [f"{PACKAGE_NAME} = {PACKAGE_NAME}.cli"],
},
install_requires=["typer"],
)
- there is also an empty
__init__.py
inbar/src/bar/
In another file foo/src/foo/cli.py
I have
import typer
import pkg_resources
app = typer.Typer()
# real program has lots of plugins
CLI_PLUGINS = {
entry_point.name: entry_point.load()
for entry_point in pkg_resources.iter_entry_points("foo.cli_plugins")
}
for name, entry_point in CLI_PLUGINS.items():
app.add_typer(entry_point.app, name=name)
@app.command()
def fizz(path: str = typer.Argument(...)):
typer.echo(f"Fizzy {path}")
This is installed with this setup.py
from foo/setup.py
"""Setup."""
from pathlib import Path
from setuptools import find_packages
from setuptools import setup
PACKAGE_NAME = "foo"
setup(
name=PACKAGE_NAME,
version="0.0.1",
package_dir={"": "src"},
packages=find_packages(where="src"),
zip_safe=False,
include_package_data=True,
python_requires=">=3.7",
entry_points={
"console_scripts": [f"{PACKAGE_NAME} = {PACKAGE_NAME}.cli:app"],
},
install_requires=["typer"],
)
- there is also an empty
__init__.py
infoo/src/foo/
When I call foo --help
I get
Usage: ex-primary [OPTIONS] COMMAND [ARGS]...
Options:
--install-completion [bash|zsh|fish|powershell|pwsh]
Install completion for the specified shell.
--show-completion [bash|zsh|fish|powershell|pwsh]
Show completion for the specified shell, to
copy it or customize the installation.
--help Show this message and exit.
Commands:
fizz
bar
which is also great. This is exactly what I want.
However..
When I call foo bar --help
I get
Usage: foo bar [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
main
Which is not what I want. I don't want to have to call main
out by name here.
Question
- Is there a way to not have
main
as a command here? I want to be able to callfoo bar PATH
instead offoo bar main PATH
.
What I want for for bar --help
is
Usage: foo bar [OPTIONS] PATH
Options:
--install-completion [bash|zsh|fish|powershell|pwsh]
Install completion for the specified shell.
--show-completion [bash|zsh|fish|powershell|pwsh]
Show completion for the specified shell, to
copy it or customize the installation.
--help Show this message and exit.
Additional context
I have tried adding a callback to the add_typer
and it almost does what I want.
Ex:
changing
app.add_typer(entry_point.app, name=name)
to
app.add_typer(entry_point.app, name=name, callback=entry_point.main)
- Doing this adds the argument of
bar
to the usage offoo bar --help
(yay) but it still leaves theCOMMAND [ARGS]
and theCommands
section. Also I don't think this is the right way to go about this.
I feel like there is a nice way to get this working and I just cannot see it.
I looked at the documentation (which is beautiful) and the example with subcommands doesn't quite match this use case. There the subcommands are designed to be called explicitly.
If there is any more information I can provide please let me know.
Thank you for your great work on typer
! Cheers