typer
typer copied to clipboard
Default value for optional multi-value argument
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
def main(names: list[str] = typer.Argument(["Peter", "Paul", "Mary"])):
for name in names:
typer.echo(f"Hi, {name}")
if __name__ == '__main__':
typer.run(main)
Description
The provided example raises the following error:
Traceback (most recent call last):
File "[...]\main.py", line 8, in <module>
typer.run(main)
TypeError: 'default' is not supported for nargs=-1.
Looking at the Click docs, my understanding is that nargs=-1 is used to denote Variadic arguments, i.e. multi-value arguments.
So if default is not supported for nargs=-1, is there any way to have default values for multi-value arguments in Typer?
Desired behaviour: the default values are used if no argument values are provided, with a help message that looks something like this:
Usage: main.py [OPTIONS] [NAMES]...
Arguments:
[NAMES]... [default: [Peter, Paul, Mary]]
Options:
--help Show this message and exit.
Operating System
Windows
Operating System Details
No response
Typer Version
0.7.0
Python Version
3.10.9
Additional Context
I believe this is related to #108, where the conclusion was to use None as default to achieve an optional multi-value argument. However, it would be useful if other default values were supported as well. But this might just be a limitation of Click?
Love the project ❤️
This may or may not help but I got around this issue by manually creating a callback that detects if the given parameter is None and setting it to my desired default value.
I also set show_default=False in foo: List[bar] = typer.Argument(...) and added "[black]\[default: default][/black]" to the end of my help="parameter help string" in order to display an accurate default message when running my-command --help.
I can post the complete code if anyone needs it but I feel it's easy enough to understand from this.
Note Rich is required to use
[color][/color]tags inside strings
Edit: nvm it works for:
def stdin_callback(value: Optional[Path]) -> Path:
return value if value else [Path('/dev/stdin')]
def stdin_callback(value: Optional[Path]) -> Path:
return value if value else Path('/dev/stdin')
def main(
files: List[Path] = typer.Argument(
default=[None].
allow_dash=True,
exists=True,
dir_okay=False,
readable=True,
callback=stdin_callback)
TypeError: 'default' is not supported for nargs=-1.
Introducing a callback doesn't solve the issue unfortunately.
Using a callback worked for me
def callback(value: Optional[list[int]]) -> list[int]:
return value if value else []
Trying to do something similar (a command that optionally takes any number of string arguments), I tried many variations, before I discovered that this is the only way that works:
names: List[str] = Argument( default = None )
However, this doesn't, even though it should mean the same thing, it still makes it required.
names: Annotated[List[str], Argument( default = None )]
These similar variations also still end up required:
names: Annotated[List[str], Argument( default = None )]
names: Annotated[List[str], Argument( default_factory = list )]
This one throws the "'default' is not supported for nargs=-1" error:
names: List[str] = Argument( default_factory = list )
And these variations do make it optional, but also turn it into a --names option instead of keeping it as an argument:
names: Annotated[List[str], Argument()] = None
names: Annotated[List[str], Argument()] = []
names: Annotated[Optional[List[str]], Argument()] = None