Skip to content
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

Default value for optional multi-value argument #518

Open
7 tasks done
mathiazom opened this issue Dec 18, 2022 · 5 comments
Open
7 tasks done

Default value for optional multi-value argument #518

mathiazom opened this issue Dec 18, 2022 · 5 comments
Labels
question Question or problem

Comments

@mathiazom
Copy link

First Check

  • I added a very descriptive title to this issue.
  • 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 read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to Typer but to Click.

Commit to Help

  • 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 ❤️

@mathiazom mathiazom added the question Question or problem label Dec 18, 2022
@N4D1K-lgtm
Copy link

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

@InCogNiTo124
Copy link

InCogNiTo124 commented Jan 28, 2023

Edit: nvm it works for:

def stdin_callback(value: Optional[Path]) -> Path:
    return value if value else [Path('/dev/stdin')]
I'm trying to make CLI which accepts multiple file paths (and stdin if no paths provided) and I get the same error.
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.

@JP-Globality
Copy link

Using a callback worked for me

def callback(value: Optional[list[int]]) -> list[int]:
    return value if value else []

@jasonk
Copy link

jasonk commented Jan 31, 2024

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

@MaxG87
Copy link

MaxG87 commented Sep 9, 2024

Using typer=0.12.5 I still can reproduce the reported issue.

However, I use another workaround that might be interesting:

def main(names: list[str] | None = typer.Argument(None)):
    if names is None:
        names = ["Peter", "Paul", "Mary"]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Question or problem
Projects
None yet
Development

No branches or pull requests

6 participants