-
-
Notifications
You must be signed in to change notification settings - Fork 664
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
Does Typer support Tuples as Multiple Multi Value Options - List[Tuple[str, str]] - like Click does? #387
Comments
I was struggling with same problem here. I'll post how I solved my problem, this could be useful for others. Once I tried to define option type as
To workaround this situation I had to use
I had to use this other pattern:
At the end, my code looks like: # Since there is a `_parse_option`
def _parse_option(value):
result = defaultdict(list)
for value in values:
k, v = value.split('=')
result[k.strip()].append(v.strip())
return result.items()
@app.command
def my_command(opts: List[str] = typer.Option(..., callback=_parse_option):
print(opts)
# [('key1', ['value1']), ('key2', ['value2'])] Note 1: Note 2: I'm using |
Another, possibly naive way of handling this would be to use the Click Context and a bit of custom parsing, though as mentioned in my own question, I don't know how to add that to the help output. This would look something like: def _parse_extras(extras: List[str]):
_extras = extras[:]
extra_options = {
'complex_option1': [],
'complex_option2': [],
}
while len(_extras) > 0:
if _extras[0] == '--complex-option1' and len(_extras) > 2:
complex_values = _extras[1:2]
_extras = _extras[3:]
extra_options['complex_option1'].append(_parse_opt1(*complex_values))
elif _extras[0] == '--complex-option2' and len(_extras) > 3:
complex_values = _extras[1:3]
_extras = _extras[4:]
extra_options['complex_option2'].append(_parse_opt2(*complex_values))
else:
raise click.NoSuchOption(_extras[0])
return extra_options
@app.command(context_settings={"allow_extra_args": True, "ignore_unknown_options": True})
def my_command(ctx: typer.Context, other_normal_opts):
extra_options = _parse_extras(ctx.args)
complex_option1 = extra_options['complex_option1']
complex_option2 = extra_options['complex_option2']
print(dir()) |
Type conversion does not work. Fixes fastapi#387.
It was pretty simple to get this working locally for something simple like |
This requires a hack to maintain backwards-compatibility (i.e. setting sys.argv and manually running a typer command to parse it), but argparse also required a hack to preprocess the arguments. This is blocked by fastapi/typer#387, which I have fixed locally (tests will not pass without typer as-is). Functional improvements: - Typer gives nicer help text and can generate completions for different shells - --length now enforces the visual length of the entire text, making it possible to have zscroll take up a consistent amount of space even if after or before padding changes - Add --shift-count flag (partially addresses #21) - Allow using --match-text ".*" "<new options>" to just check the exit status of the corresponding match command Code improvements: - Split into multiple files and organize better - Remove use of globals; instead create a Scroller class that stores used state - Remove various confusing or unnecessary variables and functions: last_hidden_was_wide, next_hidden_was_wide, pad_with_space, needs_scrolling, should_restart_printing, build_display_text, etc. - Simplify handling of full-width characters; it is only necessary to handle phasing out one side; the other side can automatically be handled when the visual length of the text is fixed (see visual_slice function that replaces make_visual_len) - Way more testing Meta improvements: - Add .editorconfig file - Use ruff for linting (remove flake8, isort, etc.) - Additionally run tests on 3.10 - Do all configuration in pyproject.toml (remove .pylintrc and tox.ini) - Switch from Makefile/make to poethepoet (fewer dependencies) - Stop using setup.py (unnecessary duplication and deprecated); use poetry (and eventually poeblix for data_files support) instead - Add a basic nix.shell file
@noctuid Hi! I think i have a simple usecase in my project - |
I changed the Typer code itself (see WIP Support specifying options as a list of tuples). I'm not sure it will work with |
@noctuid thanks for sharing this, hope it will be merged at some point! I re-thinked my approach - tuple with optional second argument is ambiguous indeed. For now it's working for me with using |
This minor fix to make @jonatasleon's worthy workaround to support also values containing the k, v = value.split('=', 1) |
Using @app.command()
def foo(pairs: list[click.Tuple] = typer.Option(click_type=click.Tuple([str, str]))):
for x, y in pairs:
print(f'x: {x}, y: {y}') |
What version of typer are you using @alex-janss? I'm getting |
0.9.0 |
This workaround works, but still it's a pitty that it does not allow type hinting to guess the type of the variable, thought. With this example both
|
I found the following workaround, with no sign of type errors by PyCharm linter / type checker. I did not check with def parse_colon_separated_pair(value: str):
return tuple(value.split(sep=':', maxsplit=2))
@app.command()
def foo(
pairs: Annotated[
Optional[List[click.Tuple]],
typer.Option(
metavar="KEY:VALUE",
parser=parse_colon_separated_pair,
] = None,
):
print(f"{type(pairs)=}, {pairs=}")
|
First Check
Commit to Help
Example Code
Description
I'm converting a small Click app to Typer and hit the following issue; I can't seem to get Tuples as Multiple Multi Value Options working.
I would like to achieve the following:
This is achieved in Click with the following option:
Operating System
Linux
Operating System Details
No response
Typer Version
0.4.1
Python Version
3.7.3
Additional Context
No response
The text was updated successfully, but these errors were encountered: