typer icon indicating copy to clipboard operation
typer copied to clipboard

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

Open PhilReinhold opened this issue 2 years ago • 29 comments

First Check

  • [X] I added a very descriptive title to this issue.
  • [X] I used the GitHub search to find a similar issue and didn't find it.
  • [X] I searched the Typer documentation, with the integrated search.
  • [X] I already searched in Google "How to X in Typer" and didn't find any information.
  • [X] I already read and followed all the tutorial in the docs and didn't find an answer.
  • [X] I already checked if it is not related to Typer but to Click.

Commit to Help

  • [X] 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 avatar Jan 12 '23 23:01 PhilReinhold

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

domef avatar Jan 13 '23 10:01 domef

@tiangolo, Any updates on this feature?

raminqaf avatar Apr 04 '23 11:04 raminqaf

Related to

  • #348

johnthagen avatar May 01 '23 18:05 johnthagen

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?

pdonorio avatar Jun 28 '23 20:06 pdonorio

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

renardeinside avatar Jul 18 '23 21:07 renardeinside

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

lucasgadams avatar Aug 06 '23 16:08 lucasgadams

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.

toppk avatar Aug 06 '23 21:08 toppk

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:
    ...

roganartu avatar Aug 07 '23 18:08 roganartu

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.

johnthagen avatar Aug 07 '23 19:08 johnthagen

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

albertotb avatar Dec 11 '23 14:12 albertotb

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

exislow avatar Jan 12 '24 09:01 exislow

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:
    ...

dolfandringa avatar Feb 21 '24 14:02 dolfandringa

Any updates on this?

funkindy avatar Jul 24 '24 10:07 funkindy

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

  • https://github.com/tiangolo/typer/issues/678#issuecomment-2090423535

johnthagen avatar Jul 24 '24 11:07 johnthagen

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

  • https://github.com/tiangolo/typer/issues/678#issuecomment-2090423535

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

hongqn avatar Jul 25 '24 06:07 hongqn

Hello all! Thanks for the feedback. :coffee:

This should have been fixed by https://github.com/fastapi/typer/pull/548

It is now available in Typer 0.12.4 :rocket:

tiangolo avatar Aug 17 '24 03:08 tiangolo

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'

tonjo avatar Aug 22 '24 17:08 tonjo

@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.

tiangolo avatar Aug 23 '24 20:08 tiangolo