Replies: 8 comments
-
|
Thanks for the code example! I tried it, but I get an error. Thoughts? min.py: import typer
from enum import Enum
from typing import Dict
class A:
pass
a = A()
networks: Dict[str, A] = {"A": a}
NetworkEnum = Enum(
"NetworkEnum",
names=[(name, network) for name, network in networks.items()],
module=__name__,
)
app = typer.Typer()
@app.command()
def main(network: NetworkEnum):
the_network = networks[network.value]
print(the_network)
if __name__ == "__main__":
app() Here's a workaround: import typer
from enum import Enum
from typing import Dict
class A:
pass
a = A()
networks: Dict[str, A] = {"A": a}
NetworkEnum = Enum(
"NetworkEnum",
names=[(name, name) for name in networks.keys()],
module=__name__,
)
app = typer.Typer()
@app.command()
def main(network: NetworkEnum):
the_network = networks[network.value]
print(the_network)
if __name__ == "__main__":
app()which results in |
Beta Was this translation helpful? Give feedback.
-
|
Right, my apologies, this must have been a typo! The point is basically that, while the existing option to map a string (= user input) to an enum member is nice, it doesn't cover the other classic kind of mapping: dictionaries, i.e. mapping (string) dictionary keys to dictionary values. This is particularly useful when the dictionary is dynamic and cannot be rewritten as an enum (at least not easily – this is what I meant to illustrate with the code example you fixed). |
Beta Was this translation helpful? Give feedback.
-
|
I feel like this is an extension of the more basic functionality which is the option to specify choices from a list. Of course you can generate an enum but I feel like this is conflicting with the whole point of typer which is to make defining CLIs clean and simple. Similar to the examples provided, we could have: import typing
import typer
value_options = ['hello', 'world']
def main(value: typing.Annotated[str, typer.Argument(choices=value_options)]):
print(value)
if __name__ == "__main__":
typer.run(main)instead of the current solution import enum
import typer
value_options = ['hello', 'world']
ValueEnum = enum.Enum('ValueEnum', {
f'value_{index}': value
for index, value in enumerate(value_options)
})
def main(value: ValueEnum):
print(value.value)
if __name__ == "__main__":
typer.run(main)I think a big benefit here is that you can correctly type-annotate the value. Related again to this would be support for import typing
import typer
def main(value: typing.Literal['hello', 'world']):
print(value.value)
if __name__ == "__main__":
typer.run(main) |
Beta Was this translation helpful? Give feedback.
-
Imho, the point of using import enum
import typer
class ValueEnum(enum.Enum):
HELLO = "hello"
WORLD = "world"
def main(value: ValueEnum) -> None:
print(value.value)
# What if I have typo here like `ValueEnum.HELLOW`?
# It will be caught either by Python itself or static analysis tools.
if value is ValueEnum.HELLO: ...
if __name__ == "__main__":
typer.run(main) |
Beta Was this translation helpful? Give feedback.
-
|
@jfcherng That's why I think supporting |
Beta Was this translation helpful? Give feedback.
-
|
@jfcherng I agree with @inkychris. Static analysis is not possible here, anyway, because the whole point is to generate the choices dynamically:
|
Beta Was this translation helpful? Give feedback.
-
|
Is there any updated guidance for doing this now? |
Beta Was this translation helpful? Give feedback.
-
|
Are there any more updates on this? For multiple choices you can set it to from pathlib import Path
import click
import typer
from typing import Annotated
app = typer.Typer()
@app.command()
def main(
input_dir: Annotated[Path, typer.Argument(help="Input directory")],
languages: Annotated[list[str], typer.Argument(
help="List of languages to include in the generated documentation",
click_type=click.Choice(["de", "en", "es", "fr"]),
)],
output_dir: Annotated[Path, typer.Option(help="Output directory")] = "out",
) -> None:
print("RUNNING MAIN", input_dir, languages, output_dir)
if __name__ == "__main__":
app()The following Actually running it now looks like this: |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I have a (dynamic) dictionary mapping strings (names) to more complicated objects. I want to define a Typer argument (or option) that allows the user to choose one of those objects by providing the corresponding name.
The solution I would like
Pseudo-code:
Basically, I would like to tell Typer to use the dictionary's string keys as the possible values the user can choose from on the command line and then map them to the corresponding NeuralNetwork object when invoking
main().Alternatives I've considered
Since Typer currently doesn't support Union types (let alone dynamically created ones), the only alternative with proper type checking and auto-completion that I've found is the following:
While this works, it requires boilerplate code and is much less readable.
Beta Was this translation helpful? Give feedback.
All reactions