fastapi / typer

Typer, build great CLIs. Easy to code. Based on Python type hints.
https://typer.tiangolo.com/
MIT License
15.44k stars 656 forks source link

unlimited argument for an option with comma spliter #534

Open Alirezaaraby opened 1 year ago

Alirezaaraby commented 1 year ago

First Check

Commit to Help

Example Code

import typer

def main(command: List[str] = typer.Option(None)):
    print(command)

Description

I want to pass unlimited multiple arguments to type.option with comma splitter like below.

$ python cli.py add test -C "hi I'm test", "hello this is test"

I want to get a list like ["hi I'm test","hello this is test"] from the above input.

Operating System

Windows

Operating System Details

Windows Version 10.0.19045.2486

Typer Version

0.7.0

Python Version

Python 3.9.1

Additional Context

No response

heiskane commented 1 year ago

As far as i understand the intended way to use a list type parameter would be like this:

$ python app.py --command arg1 --command arg2
['arg1', 'arg2']

If you really want to use comma separated strings you could do the following:

from typing import Annotated

import typer

def comma_list(raw: str) -> list[str]:
    return raw.split(",")

def main(
    command: Annotated[list, typer.Option(parser=comma_list)],
):
    print(command)

if __name__ == "__main__":
    typer.run(main)
$ python potato/main.py --command "hello","world"
['hello', 'world']

Note that using list[str] instead of list for the command parameter seems to attempt some type conversion magic resulting in a nested list so this isn't perfect. Personally I would just keep it simple and do something like this instead:

import typer

def main(commands: str = typer.Option()):
    cmd_list = commands.split(",")
    print(cmd_list)

if __name__ == "__main__":
    typer.run(main)
devraj commented 2 months ago

I implemented something quite similar in my cli where it takes multiple values as part of an argument in a standard unix fashion i.e separated by spaces.

Essentially extending on what @heiskane is suggesting you do for Option you can define an argument as follows:

from typing import Optional, List
from typing_extensions import Annotated

import typer

@app.command("ack")
async def acknowledge(
    ids: Annotated[List[int], typer.Argument(help="alarm id")], #multiple ids
    message: Annotated[
        Optional[str],
        typer.Option(
            "-m",
            "--message",
            help="comment to add to history",
        ),
    ] = None,
):
    for id in ids:
        print(id)

See issue on my project for more detail.