pydantic: Pydantic fails with Python 3.10 new UnionType
Checks
- I added a descriptive title to this issue
- I have searched (google, github) for similar issues and couldn’t find anything
- I have read and followed the docs and still think this is a bug
Bug
Pydantic fails with a new-style unios, presented in Python 3.10. The reason is straightforward - it simply doesn’t know about UnionType.
The problem is here: pydantic.fields.py:529
where we check the origin
for type Union
.
Since the new UnionType
is compatible with typing.Union
(according to documentation) it’s probably should be easy to fix. I could do this, but I need to understand, what could be affected by this. Is it will be enough to check all usages of Union type and fix it carefully? Also, it’s probably should be some kind of a function is_union
to make it work for versions before 3.10.
Additional info
Output of python -c "import pydantic.utils; print(pydantic.utils.version_info())"
:
pydantic version: 1.8.2
pydantic compiled: False
install path: /Users/drjackild/.pyenv/versions/3.10.0/envs/groups/lib/python3.10/site-packages/pydantic
python version: 3.10.0 (default, Oct 7 2021, 17:08:09) [Clang 13.0.0 (clang-1300.0.29.3)]
platform: macOS-11.6-x86_64-i386-64bit
optional deps. installed: ['typing-extensions']
from pydantic import BaseModel
class A(BaseModel):
s: str | None
Raises exception:
Traceback (most recent call last):
File "/Users/drjackild/.pyenv/versions/groups/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3444, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-16-a6ee5ea43ee8>", line 1, in <module>
class A(BaseModel):
File "/Users/drjackild/.pyenv/versions/groups/lib/python3.10/site-packages/pydantic/main.py", line 299, in __new__
fields[ann_name] = ModelField.infer(
File "/Users/drjackild/.pyenv/versions/groups/lib/python3.10/site-packages/pydantic/fields.py", line 411, in infer
return cls(
File "/Users/drjackild/.pyenv/versions/groups/lib/python3.10/site-packages/pydantic/fields.py", line 342, in __init__
self.prepare()
File "/Users/drjackild/.pyenv/versions/groups/lib/python3.10/site-packages/pydantic/fields.py", line 451, in prepare
self._type_analysis()
File "/Users/drjackild/.pyenv/versions/groups/lib/python3.10/site-packages/pydantic/fields.py", line 626, in _type_analysis
raise TypeError(f'Fields of type "{origin}" are not supported.')
TypeError: Fields of type "<class 'types.UnionType'>" are not supported.
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Reactions: 17
- Comments: 24 (8 by maintainers)
Commits related to this issue
- unittest: Disabled tests with `UnionType` as pydantic can't do that yet. *sigh* See: https://github.com/samuelcolvin/pydantic/issues/3300 — committed to luckydonald/fastorm by luckydonald 3 years ago
- Fix #34 (#44) You can see in the changes, that I alternate between the `typing.Optional` and new union operator `|`, which introduces inconsistency. This is because: 1. It seems the union operato... — committed to apptension/onetimepass by Toreno96 2 years ago
- Update type annotations for Python 3.10 This commit will update type annotation syntax for Python 3.10. The project currently also supports Python 3.8 and 3.9, so the annotations are imported with `f... — committed to br3ndonland/inboard by br3ndonland 3 years ago
I’ll try to get a new release out asap.
Hi everyone You need to understand that
int | str
is not valid at runtime for python < 3.10 as type doesn’t implement__or__
. It is valid at interpretation time by addingfrom __future__ import annotations
but pydantic can’t do magic and change how python works at runtime. I opened a while ago https://github.com/samuelcolvin/pydantic/pull/2609, which works for python < 3.10 by using https://github.com/PrettyWood/future-typing, which changes at interpretation timeint | str
intoUnion[int, str]
for example. But I don’t think it’s worth merging as moving to python 3.10 is safer Hope it clarifies thingsIf I may, I’d like to discourage people from posting comments like “+1”.
The issue is already confirmed and acknowledged by maintainer, fix will be available with the next release: https://github.com/samuelcolvin/pydantic/issues/3300#issuecomment-938145241.
These comments are noisy for everyone subscribed to this issue, while providing no value at all.
You can subscribe to this issue if you wish to be notified when it will be fixed.
In the meanwhile you can use this as a workaround: https://github.com/samuelcolvin/pydantic/issues/3300#issuecomment-950122191.
@Bobronium sorry for the noise then.
For me personally, “+1” comments are having the value of noticing the maintainer there are actually people interested in the fix (and I see this practice is very common in open-source projects, so I wasn’t aware this can be bad), especially considering the #3300 (comment) was actually from a few months ago and there’s still no release yet.
But you’re arguments are perfectly valid and if you see the alternative solution for that (do you see if and how many people subscribe to the issue?) then I’m happy to oblige 😅
Sorry for reopening the issue, and sorry if that’s not the best place to ask this question, but IMHO it’s pretty related:
Python 3.7 also supports the new UnionType if
from __future__ import annotations
is used:But it does not work in Pydantic 1.9:
example.py
Is it something that could be supported in the future, or such backward compatibility is out of scope?
+1
Here’s a workaround until it gets released.
Same approach code also works for SQLModel, but instead of inheriting from
BaseModel
andMyModelMetaclass
, you’ll need to inherit fromSQLModel
andSQLModelMetaclass
Update: rather fragile addition that also supports ForwardRef's
@Toreno96, no worries. Sorry if my comment read too grumpy 🙆🏻.
Let me elaborate on why I personally think that “+1” comments are usually a bad idea.
Let’s start with better alternative first: reactions. I think it’s a great feature for showing the number of people agree/interested/grateful/upset with a comment/issue. Unlike comments, it doesn’t increase the amount of noise and saves the issue from being flooded: imagine googling this issue and seeing 30+ comments with plain “+1” or “same here”. It’s not hard to imagine that people won’t even attempt to read the rest of the comments, and instead will post yet another “+1” and leave.
While I understand your concern and desire to give a notice to the maintainer, I don’t think “+1” would be the best approach for that. I believe maintainers are already very aware of this, and many other issues. I think some form of discussion here would be more applicable if, for example,
pydantic
had a new release that didn’t address this issue. But currently, It’s not a question “whether this issue will be fixed”, or even “when” (fix is already inmaster
), but “when will see the new release”.So I’m not discouraging any conversations, I just believe that “+1” is not a great start for one, especially in given situation 😃
+1 on this
Hi guys, I believe the fix suggested in https://github.com/samuelcolvin/pydantic/issues/3300#issuecomment-950122191 will not be working for the following case:
shouldn’t we replace
=
with:
when we define variables in model?a = Optional[str]
->a: Optional[str]
+1 on this
I’m actually experiencing this on Python
3.10.4
with Pydantic1.10.1
.results in:
Adding the following to
PromptData
results in
I remember running into this problem when testing 3.10 with Pydantic and found this topic. It appears like it’s no longer a problem with the release of 1.9 (https://github.com/samuelcolvin/pydantic/pull/2885), so the problem is resolved.
I think there’s a related bug that, based on the existing fix in master, I think would also be fixed by this. I wanted to share the trace here in case it helps someone googling, but didn’t think it was worthy of a separate issue. If you disagree let me know and I’ll raise one.
Given:
One gets:
The JSON error confused me when googling this initially after updating a FastAPI project of mine to 3.10 and making no other changes. I believe it happens because the lack of support for
UnionType
breaks thefield.is_complex()
check, causing pydantic to attempt to parse it as JSON.