Relationship with nullable forward reference (TYPE_CHECKING) fails on Python 3.14 (PEP 649) #1765
Unanswered
romver2002
asked this question in
Questions
Replies: 1 comment
-
Full tracebackFile "sqlmodel/main.py", line 550, in __new__
original_annotations = get_annotations(class_dict)
File "sqlmodel/_compat.py", line 110, in get_annotations
raw_annotations = call_annotate_function(annotate, format=Format.FORWARDREF)
File "annotationlib.py", line 820, in call_annotate_function
result = func(Format.VALUE_WITH_FAKE_GLOBALS)
File "reproducer.py", line 23, in __annotate__
schedule: Schedule | None = Relationship(
~~~~~~~~^~~~~~
TypeError: unsupported operand type(s) for |: 'ForwardRef' and 'NoneType'
```\ |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
First Check
Commit to Help
Example Code
Description
When using
Relationshipfields with models imported underTYPE_CHECKING(common pattern for circular imports), SQLModel fails on Python 3.14 due to incompatible handling of PEP 649 deferred annotations in_compat.py.Root cause
In
sqlmodel/_compat.py, theget_annotations()function calls:With Python 3.14's PEP 649,
Annotation | What happens | Result -- | -- | -- schedule: Schedule \| None | ForwardRef("Schedule") \| None returned | TypeError: unsupported operand type(s) for \|: 'ForwardRef' and 'NoneType' schedule: "Schedule" \| None | Python evaluates str.__or__(NoneType) | TypeError: unsupported operand type(s) for \|: 'str' and 'NoneType' schedule: "Schedule \| None" | String literal passed through | Works (current workaround)call_annotate_functioninternally falls back toFormat.VALUE_WITH_FAKE_GLOBALS, which executes the annotation expressions. For nullable relationships with forward references, this creates issues:Why this worked before Python 3.14
Before Python 3.14, projects used
from __future__ import annotations(PEP 563) which turned all annotations into strings.typing.get_type_hints()resolved them lazily after all modules were loaded. Python 3.14 deprecated PEP 563 in favor of PEP 649, but SQLModel's_compat.pydoesn't fully handle the newForwardRefobjects.Current workaround
Wrap the entire annotation in quotes:
Suggested fix
In
_compat.py, theget_annotations()function should handleForwardRefobjects in union types returned bycall_annotate_function, similar to howget_relationship_to()already does:Operating System
Linux, Windows
Operating System Details
No response
SQLModel Version
0.0.34
Python Version
3.14
Additional Context
Related FastAPI discussion with a similar fix: fastapi/fastapi#14784
SQLAlchemy 2.0.39+ already fixed their PEP 649 compatibility, but SQLModel has its own annotation processing layer in _compat.py
Beta Was this translation helpful? Give feedback.
All reactions