-
First Check
Commit to Help
Example Codeimport typer
from typer import Option
from typing import Annotated
app = typer.Typer()
def parse_union(item: str) -> int | str:
if item.isdigit():
return int(item)
return item
@app.command()
def main(items: Annotated[list[int | str], Option(parser=parse_union)] = []):
print(items)
if __name__ == "__main__":
app() DescriptionAfter upgrading to typer Previous (Expected?) Behavior (typer $ python example.py --items 4
[4] Actual Behavior (typer $ python example.py --items 4
AssertionError: List types with complex sub-types are not currently supported Analysis: This regression seems to have been introduced in commit 218bf89, where additional checks were added to the type parsing logic.
Reproducer: Here is a minimal example to demonstrate the difference between the two versions: from typer._typing import get_args
from typing import List
from typing import get_origin as _typing_get_origin
problematic_type = List[str | int]
# typer 0.12.3
# https://github.com/fastapi/typer/blob/525c7779ba8914b3950b26ffa21decdfd3eec6ec/typer/main.py#L841-L846
main_type = problematic_type.__args__[0]
print(main_type)
result = getattr(str | int, "__origin__", None)
print(result)
assert not result
# typer 0.12.4
# https://github.com/fastapi/typer/blob/29d1d1fafe92d27b37462c90d67ce8687bab0341/typer/main.py#L843-L848
main_type = get_args(problematic_type)[0]
print(main_type)
result = _typing_get_origin(main_type)
print(result)
assert not result # Will trigger an assertion error! Question: Is this behavior change expected in typer Thanks Operating SystemLinux Operating System DetailsNo response Typer Version0.12.4 Python Version3.11.9 Additional ContextNo response |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 1 reply
-
Thanks for the report! We'll look into this 🙏 |
Beta Was this translation helpful? Give feedback.
-
I'm still looking into this, but to come back to
That was indeed the case, because To make things worse, the Using the Let me know if the above makes sense to you or not. If it does - I'm starting to wonder whether your use-case from the example code snippet sort of "worked by accident" in |
Beta Was this translation helpful? Give feedback.
-
Thanks a lot for the detailed explanation 🙂
Yes I agree - it does feel more consistent overall, and seems to be the right approach.
Yes I also think that it "worked by accident", but it worked nonetheless. This makes me wonder whether it makes sense to completely prevent union of simple types. However, I don't have enough insight to say where the boundary should be set. Any opinion? If you think it makes sense, then I could just write the following code and the issue would be solved: import typer
from typer import Option
from typing import Annotated
app = typer.Typer()
def parse_union(item: str) -> int | str:
if item.isdigit():
return int(item)
return item
@app.command()
def main(items: list[str] = []):
items = [parse_union(item) for item in items]
print(items)
if __name__ == "__main__":
app() |
Beta Was this translation helpful? Give feedback.
Thanks a lot for the detailed explanation 🙂
Yes I agree - it does feel more consistent overall, and seems to be the right approach.
Yes I also think that it "worked by accident", but it worked nonetheless. This makes me wonder whether it makes sense to completely