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

Using some_type | None syntax for type annotations causes error in python 3.11 #533

Closed
7 tasks done
PhilReinhold opened this issue Jan 12, 2023 · 30 comments
Closed
7 tasks done
Labels
bug Something isn't working

Comments

@PhilReinhold
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
from datetime import datetime
print(typer.__version__)

app = typer.Typer()

@app.command()
def f(x: datetime | None = None):
    print(x)

if __name__ == "__main__":
    app()

Description

In python 3.10 this script runs as expected, but on 3.11 I get the following error

Traceback (most recent call last):
  File "/Users/pcrein/qdash/test.py", line 16, in <module>
    app()
  File "/Users/pcrein/.pyenv/versions/qdash-3.11.0/lib/python3.11/site-packages/typer/main.py", line 328, in __call__
    raise e
  File "/Users/pcrein/.pyenv/versions/qdash-3.11.0/lib/python3.11/site-packages/typer/main.py", line 311, in __call__
    return get_command(self)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^
  File "/Users/pcrein/.pyenv/versions/qdash-3.11.0/lib/python3.11/site-packages/typer/main.py", line 364, in get_command
    click_command = get_command_from_info(
                    ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pcrein/.pyenv/versions/qdash-3.11.0/lib/python3.11/site-packages/typer/main.py", line 577, in get_command_from_info
    ) = get_params_convertors_ctx_param_name_from_function(command_info.callback)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pcrein/.pyenv/versions/qdash-3.11.0/lib/python3.11/site-packages/typer/main.py", line 553, in get_params_convertors_ctx_param_name_from_function
    click_param, convertor = get_click_param(param)
                             ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/pcrein/.pyenv/versions/qdash-3.11.0/lib/python3.11/site-packages/typer/main.py", line 844, in get_click_param
    parameter_type = get_click_type(
                     ^^^^^^^^^^^^^^^
  File "/Users/pcrein/.pyenv/versions/qdash-3.11.0/lib/python3.11/site-packages/typer/main.py", line 773, in get_click_type
    raise RuntimeError(f"Type not yet supported: {annotation}")  # pragma no cover
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: Type not yet supported: datetime.datetime | None

Operating System

macOS

Operating System Details

No response

Typer Version

0.7.0

Python Version

3.11.0

Additional Context

I believe the issue is that for some reason, typing.get_type_hints is no longer converting the UnionType into a Union. I would suggest as a fix that get_click_param use typing.get_origin/get_args instead of __args__ and __origin__ attributes.

@PhilReinhold PhilReinhold added the question Question or problem label Jan 12, 2023
@domef
Copy link

domef commented Jan 13, 2023

I'm having the same issue. Using Union[type1, type2] with from typing import Union leads to the same error.

@raminqaf
Copy link

raminqaf commented Apr 4, 2023

@tiangolo, Any updates on this feature?

@johnthagen
Copy link

Related to

@pdonorio
Copy link

Hi and thanks a lot for this great library!

I've run into this issue as well. May someone provide some guidance on what should be fixed,
in case someone would like to contribute to solve it?

@renardeinside
Copy link

I have the same issue with Python 3.10. Any advisory on how to fix this?

@lucasgadams
Copy link

Switching from str | None = None to Optional[str] = None worked for me.

@toppk
Copy link

toppk commented Aug 6, 2023

Switching from str | None = None to Optional[str] = None worked for me.

That is a pretty good workaround, but for me it means that I have to configure my linters one way for modules that use typer and another way for the rest of the code.

@roganartu
Copy link

Switching from str | None = None to Optional[str] = None worked for me.

That is a pretty good workaround, but for me it means that I have to configure my linters one way for modules that use typer and another way for the rest of the code.

pyupgrade will respect type aliases, as will typer. It's unwieldy, but this means you can do the following without having to configure pyupgrade to keep runtime types:

OptionalStr = Optional[str]

main(
    foo: OptionalStr = typer.Argument(None),
) -> None:
    ...

@johnthagen
Copy link

For me, I'd rather use Optional[str] than making an alias named OptionalStr (that someone then has to look up to know exactly how it was defined).

I think the idea here is that we ideally want to use str | None as is the new, preferred style, that pyupgrade/Ruff also lint for.

fstagni added a commit to fstagni/diracx that referenced this issue Sep 4, 2023
fstagni added a commit to fstagni/diracx that referenced this issue Sep 5, 2023
fstagni added a commit to fstagni/diracx that referenced this issue Sep 6, 2023
fstagni added a commit to fstagni/diracx that referenced this issue Sep 6, 2023
fstagni added a commit to fstagni/diracx that referenced this issue Sep 8, 2023
@albertotb
Copy link

albertotb commented Dec 11, 2023

For me, I'd rather use Optional[str] than making an alias named OptionalStr (that someone then has to look up to know exactly how it was defined).

I think the idea here is that we ideally want to use str | None as is the new, preferred style, that pyupgrade/Ruff also lint for.

Agree, for now I've just disabled linting in that specific line, in Ruff you can do it with #noqa: UP007

@exislow
Copy link

exislow commented Jan 12, 2024

It would be great to use str | None instead of Optional[]. @tiangolo: Any plans to implement this to support >= Python3.11?

@dolfandringa
Copy link

I am stuck between Typer not supporting the new python syntax with |None=None and pyupgrade refusing to add an option to skip code for a specific line. I guess I'll try this solution.....

Switching from str | None = None to Optional[str] = None worked for me.

That is a pretty good workaround, but for me it means that I have to configure my linters one way for modules that use typer and another way for the rest of the code.

pyupgrade will respect type aliases, as will typer. It's unwieldy, but this means you can do the following without having to configure pyupgrade to keep runtime types:

OptionalStr = Optional[str]

main(
    foo: OptionalStr = typer.Argument(None),
) -> None:
    ...

@funkindy
Copy link

Any updates on this?

@johnthagen
Copy link

@funkindy Waiting for @tiangolo to have time to review the MRs listed here

@hongqn
Copy link

hongqn commented Jul 25, 2024

@funkindy Waiting for @tiangolo to have time to review the MRs listed here

Obviously @tiangolo either has no time, or has no will.

@tiangolo
Copy link
Member

tiangolo commented Aug 17, 2024

Hello all! Thanks for the feedback. ☕

This should have been fixed by #548

It is now available in Typer 0.12.4 🚀

@tonjo
Copy link

tonjo commented Aug 22, 2024

Hello all! Thanks for the feedback. ☕

This should have been fixed by #548

It is now available in Typer 0.12.4 🚀

Just upgraded to Typer 0.12.4 but still same error:

TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'

@tiangolo
Copy link
Member

@tonjo please create a new discussion filling all the form data, including a minimal reproducible example we can copy and run to see your error.

Doing it with the original issue seems solved, so there's probably something different in your use case.

@pdonorio
Copy link

Hello all! Thanks for the feedback. ☕
This should have been fixed by #548
It is now available in Typer 0.12.4 🚀

Just upgraded to Typer 0.12.4 but still same error:

TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'

Hey @tonjo have you created a new issue?

I'm getting the same issue if I try the operand inside "Annotated"

app.command('some')
def some(
  option: Annotated[list | str, typer.Option()] = None,
):
   ...

In case I can open a new Issue if you didn't do it yet

@github-actions github-actions bot removed the answered label Aug 29, 2024
@pypae
Copy link

pypae commented Aug 29, 2024

@tonjo
Are you on Python 3.10+? Union type expressions were added in 3.10.

If you're getting the following error, you're probably on an earlier version:

TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'


@pdonorio
I think there are two issues with your example:

  • If I understand correctly, typer does still not support all Union types, only Optional X | None using the 3.10+ syntax. The wording in the release notes were not very clear about that.
  • list cannot be used without a specific type for the list items. e.g. list[str]
app.command('some')
def some(
  option: Annotated[list | str, typer.Option()] = None,  # the following would work: option: Annotated[list[str] | None, typer.Option()] 
):
   ...

@pdonorio
Copy link

  • If I understand correctly, typer does still not support all Union types, only Optional X | None using the 3.10+ syntax. The wording in the release notes were not very clear about that.
  • list cannot be used without a specific type for the list items. e.g. list[str]

Thanks, using list[str] on the latest works @pypae 🙏

@tonjo
Copy link

tonjo commented Aug 29, 2024

@pypae I'm using python 3.12.

Not using typer directly, though, it's an issue with sqlmodel, when defining an optional
field using a string (because that model is not defined yet), that error is thrown.

But maybe it is not the right place to comment to, as @tiangolo suggested.

@TiansuYu
Copy link

Somehow still broken under python 3.10, typer 0.12.5.

max_tokens: Annotated[int | None, typer.Option(help="Max tokens")] = None,
max_tokens
  Input should be a valid integer [type=int_type, input_value=None, input_type=NoneType]
    For further information visit https://errors.pydantic.dev/2.9/v/int_type

Somehow the validation did not pass to pydantic correctly

@NikosAlexandris
Copy link

❯ python --version
Python 3.11.9

and

❯ ipython -c "import typer; print(typer.__version__)"
0.12.5

or indeed version 0.12.4 as well, I still get

AssertionError: Typer Currently doesn't support Union types

Downgrading back to 0.12.3 complains then about

RuntimeError: Type not yet supported: float | None

Though, I have more "unions", also in the form of X | Y | None.

@NikosAlexandris
Copy link

Somehow still broken under python 3.10, typer 0.12.5.

max_tokens: Annotated[int | None, typer.Option(help="Max tokens")] = None,
max_tokens
  Input should be a valid integer [type=int_type, input_value=None, input_type=NoneType]
    For further information visit https://errors.pydantic.dev/2.9/v/int_type

Somehow the validation did not pass to pydantic correctly

This means you are bound to Python 3.10 ? In 3.11 (which is how this thread begun) it works.

@TiansuYu
Copy link

TiansuYu commented Oct 9, 2024

If I remember correctly, the <type> | None syntax is introduced in 3.10. Therefore Typer should test it against 3.10 as well IMO. But it seems that withTyper 0.12.5, I no longer has this issue any more.

@NikosAlexandris
Copy link

NikosAlexandris commented Oct 9, 2024

❯ python --version
Python 3.11.9

and

❯ ipython -c "import typer; print(typer.__version__)"
0.12.5

or indeed version 0.12.4 as well, I still get

AssertionError: Typer Currently doesn't support Union types

Downgrading back to 0.12.3 complains then about

RuntimeError: Type not yet supported: float | None

Though, I have more "unions", also in the form of X | Y | None.

Indeed the problem here is more complex unions. I removed these for now and it works. But why not add support for it ?

@TiansuYu
Copy link

TiansuYu commented Oct 9, 2024

Indeed the problem here is more complex unions. I removed these for now and it works. But why not add support for it ?

Indeed, i think Typer should support it (at least for all current python type annotations). In the future, i would hope that it supports all pydantic basemodel types as well.

@NikosAlexandris
Copy link

I think a "fix" would need to go around https://github.com/fastapi/typer/blob/master/typer/main.py#L844-L852.

@svlandeg
Copy link
Member

Closing this issue as the original topic/problem has been addressed by #548. Thanks for all the discussion! If there are remaining/related issues, please open a new discussion thread - it's easier to have one specific topic per thread 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet