typer
typer copied to clipboard
Main app with no commands no longer shows callback docstring in v0.4.0
First Check
- [X] I added a very descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the Typer documentation, with the integrated search.
- [X] I already searched in Google "How to X in Typer" and didn't find any information.
- [X] I already read and followed all the tutorial in the docs and didn't find an answer.
- [X] I already checked if it is not related to Typer but to Click.
Commit to Help
- [X] I commit to help with one of those options 👆
Example Code
import typer
app = typer.Typer()
@app.callback()
def main():
"""Main program help text."""
pass
@app.command()
def foo():
"""foo command help text."""
typer.echo("Executed foo.")
if __name__ == "__main__":
app()
Description
Not sure if this should be considered a bug or enhancement, since this was an undocumented change that could potentially be considered a regression.
- In typer<=0.3.2, when the main application is executed without any commands, it would previously print the help text from the callback and exit with status code 0.
- In typer 0.4.0, this same behavior prints only
Try 'app.py --help' for help.
andError: Missing command.
and exits with status code 2.
I'm fine with the status code change—it does seem to me like an improvement that it has an error code. Thank you for this change. (Though it would be nice for this to be documented in the change log.)
However, I think it would be useful to also directly print the help text without requiring users to make another invocation with --help
, as was the previous behavior. The git
program, for example, will exit with status 1 but also print the help text when used without any commands.
This appears to be only a typer change, and not a click change, as typer 0.4.0 behaves the same with way both click 8.0.1 and click 7.1.2.
typer 0.4.0; click 8.0.1
❯ python -c "import typer; print('typer', typer.__version__); import click; print('click', click.__version__)"
typer 0.4.0
click 8.0.1
❯ python app.py
Usage: app.py [OPTIONS] COMMAND [ARGS]...
Try 'app.py --help' for help.
Error: Missing command.
❯ echo $?
2
typer 0.4.0; click 7.1.2
❯ python -c "import typer; print('typer', typer.__version__); import click; print('click', click.__version__)"
typer 0.4.0
click 7.1.2
❯ python app.py
Usage: app.py [OPTIONS] COMMAND [ARGS]...
Try 'app.py --help' for help.
Error: Missing command.
❯ echo $?
2
typer 0.3.2; click 7.1.2
❯ python -c "import typer; print('typer', typer.__version__); import click; print('click', click.__version__)"
typer 0.3.2
click 7.1.2
❯ python app.py
Usage: app.py [OPTIONS] COMMAND [ARGS]...
Main program help text.
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:
foo foo command help text.
❯ echo $?
0
Operating System
macOS
Operating System Details
No response
Typer Version
0.4.0
Python Version
3.8.10
Additional Context
No response
I am facing the same bug. It is very annoying. Have to stick to 0.3.2 due to this. Took 5 hours of lazy exploration to find out what's going on.
@tiangolo - could you kindly comment if this is an expected change in functionality, or a bug og 0.4.0? If it's expected, is there any chance to get the old functionality back? :)
Thanks!
You can use the no_args_is_help
for this behaviour:
cli = typer.Typer(no_args_is_help=True)
Aha, the no_args_is_help
argument does recover the old behavior. Thanks! However, it looks like then that the command's status code is 0 (the way it was before). Is there no way to get both the help printed and also have a non-zero status code? I think the status code change is an improvement that I would also like to keep.
I do think that help strings, especially if not directly invoked with --help
but maybe either way, should exit with status 2.
Is there a way to set no_args_is_help=True
globally? Currently, the only workarounds I can think of are:
- Set this parameter for every instance of Typer and command. This isn't ideal since it adds quite a lot of clutter.
- Create a wrapper for the Typer class. It would look something like this and be used everywhere instead:
# typer_wrapper.py
import typer
def Typer():
return typer.Typer(no_args_is_help=True)
- CMD+click into typer.Typer and change the default value for
no_args_is_help
parameters in site-packages installed locally. Of course this only makes sense if you're using the scripts locally and not planning on packaging your CLI.
no_args_is_help: bool = True,
It would be good if there is a way to set it one time globally. I like being able to see the help text and command list without having to type --help
every time.
But there is an issue where zero argument commands don't get executed so maybe defaulting no_args_is_help to False is by design...
I am facing the same issue described by @jayqi, I have the following app:
app = typer.Typer()
@app.command()
def main(
config_file: Optional[Path] = typer.Option(None),
metrics: Optional[MetricsOptions] = typer.Option(None, help=""),
input_file: Optional[Path] = typer.Option(
None,
exists=True,
file_okay=True
),
source: Optional[str] = typer.Option(None),
fetch: Optional[str] = typer.Option(None)
):
if __name__ == "__main__":
app()
If I run the app without arguments (pipenv run python ./omnim/src/cli/app.py), I get the following:
TypeError: stat: path should be string, bytes, os.PathLike or integer, not NoneType
Such behavior is kinda expected, as I have a argument that is a file and it must exists(based on the configuration of the app), therefore, if I update the type app to the following:
app = typer.Typer(no_args_is_help=True, invoke_without_command=True)
I would expect that to show up the help message, but this is not what is happening, I still get the error about the file.
I tried different approaches but it seems none is working, any considerations why?
The code is available at https://github.com/shadowman/omni-metric
@marabesi I suspect that your error may be a different problem. I think it's coming from input_file
having a default of None
but requiring exists=True
. It seems like maybe typer is running os.stat
to check for that path's existence but it's getting run on None
. This probably should be a new, separate issue—it seems like this is an uncaught error, and either typer should convert None
to Path()
and still work, or it should give a typer error that says the value for input_file
is not a valid path.
thanks for the heads up @jayqi
Could we change the default of no_args_is_help
to True
? actually is pretty handy to get the help when no args are passed.
Maybe we should add no_args_is_help
in the docs? Maybe on this page https://typer.tiangolo.com/tutorial/options/help/ had to search quite a wile to find this thread :)
These should be the default args in my opinion:
context_settings={"help_option_names": ["-h", "--help"]},
no_args_is_help=True,
#201
+1 for the regression behavior, and vote on the default True behavior for no_args_is_help
.