Skip to content

Commit d2a7ce1

Browse files
authored
feat: Build for Python 3.14 (#383)
1 parent 9ae5d56 commit d2a7ce1

20 files changed

Lines changed: 3123 additions & 2610 deletions

.github/workflows/build-and-test.yml

Lines changed: 191 additions & 41 deletions
Large diffs are not rendered by default.

.github/workflows/build-packages.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
python-version: "3.12"
4444

4545
- name: Install uv
46-
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
46+
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
4747
with:
4848
version: "0.11.3"
4949

@@ -83,7 +83,7 @@ jobs:
8383
python-version: ${{ matrix.python-version }}
8484

8585
- name: Install uv
86-
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
86+
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
8787
with:
8888
version: "0.11.3"
8989

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ jobs:
126126
python-version: 3.12
127127

128128
- name: Install uv
129-
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
129+
uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
130130
with:
131131
version: "0.11.3"
132132

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ An Object Graph Mapper or OGM provides a developer-friendly workflow that allows
1717

1818
### Prerequisites
1919

20-
- **Python 3.10 - 3.12**
20+
- **Python 3.10+**
2121
- [`pymgclient`](https://github.com/memgraph/pymgclient):
2222

2323
- Install `pymgclient` [build prerequisites](https://memgraph.github.io/pymgclient/introduction.html#build-prerequisites)
@@ -27,8 +27,10 @@ An Object Graph Mapper or OGM provides a developer-friendly workflow that allows
2727
pip install --user pymgclient
2828
```
2929

30-
> [!WARNING]
31-
> Python 3.11 users: On Windows, GQLAlchemy is not yet compatible with this Python version. Linux users can install GQLAlchemy **without** the DGL extra (due to its dependencies not supporting Python 3.11 yet). If this is currently a blocker for you, please let us know by [opening an issue](https://github.com/memgraph/gqlalchemy/issues).
30+
> [!NOTE]
31+
> GQLAlchemy is tested on Python `3.10` through `3.14` in CI. Some optional extras
32+
> (for example TensorFlow/TF-GNN stacks) are available only for a subset of Python
33+
> versions due to upstream wheel availability.
3234
3335
### Install GQLAlchemy
3436

docs/how-to-guides/ogm.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ class Streamer(User):
231231

232232
The above class definition is not ideal, since it is not enforcing schema as before. To do that, [add constraints](#create-constraints).
233233

234+
!!! note
235+
In the current Pydantic-compatible OGM behavior, omitted nullable fields
236+
(`Optional[...]`) can be treated as `None` during model initialization.
237+
This preserves historical GQLAlchemy usage patterns, but differs from strict
238+
"required but nullable" validation semantics some users may expect.
239+
234240
If you try loading the node again, the following code:
235241

236242
```python

docs/index.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ An Object Graph Mapper or OGM provides a developer-friendly workflow for writing
1313
Either install GQLAlchemy through [pip](installation.md#pip) or [build it from
1414
source](installation.md#source). If you are using [Conda](https://docs.conda.io/en/latest/) for Python environment management, you can install GQLAlchemy through [pip](installation.md#pip).
1515

16-
!!! danger
17-
Python 3.11 users: On Windows, GQLAlchemy is not yet compatible with this Python version. Linux users can install GQLAlchemy **without** the DGL extra (due to its dependencies not supporting Python 3.11 yet). If this is currently a blocker for you, please let us know by [opening an issue](https://github.com/memgraph/gqlalchemy/issues).
16+
!!! note
17+
GQLAlchemy supports modern Python versions (including Python `3.14`), with CI
18+
coverage for `3.10` through `3.14`. Some optional extras may have narrower
19+
version support based on upstream dependencies.
1820

1921
### 2. Connect to Memgraph
2022

docs/installation.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ as pip and uv, and by building it from source.
77

88
To install GQLAlchemy, you will need the following:
99

10-
- **Python 3.10 - 3.12**
10+
- **Python 3.10+**
1111
- [`pymgclient`](https://github.com/memgraph/pymgclient):
1212

1313
* Install `pymgclient` [build prerequisites](https://memgraph.github.io/pymgclient/introduction.html#build-prerequisites)
@@ -17,8 +17,10 @@ To install GQLAlchemy, you will need the following:
1717
pip install --user pymgclient
1818
```
1919

20-
!!! danger
21-
Python 3.11 users: On Windows, GQLAlchemy is not yet compatible with this Python version. If this is currently a blocker for you, please let us know by [opening an issue](https://github.com/memgraph/gqlalchemy/issues).
20+
!!! note
21+
GQLAlchemy is tested on Python `3.10` through `3.14` in CI. Some optional extras
22+
(for example TensorFlow/TF-GNN stacks) are available only for a subset of Python
23+
versions due to upstream wheel availability.
2224

2325
## Install with pip
2426

docs/reference/gqlalchemy/models.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ def parse_obj(cls, obj)
156156
Used to convert a dictionary object into the appropriate
157157
GraphObject.
158158

159+
Compatibility note: `parse_obj` is retained for backward compatibility.
160+
For Pydantic v2-style usage, prefer `model_validate`.
161+
159162
## NodeMetaclass Objects
160163

161164
```python

gqlalchemy/__init__.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
# limitations under the License.
1414

1515
import warnings
16-
17-
from pydantic.v1 import validator # noqa F401
16+
from pydantic import validator as _validator # noqa F401
1817

1918
from gqlalchemy.models import ( # noqa F401
2019
MemgraphConstraintExists,
@@ -64,6 +63,7 @@
6463

6564
warnings.filterwarnings("once", category=GQLAlchemyWarning)
6665
__all__ = ["Memgraph"]
66+
_validator_deprecation_warning_shown = False
6767

6868
call = Call
6969
create = Create
@@ -75,3 +75,18 @@
7575
return_ = Return
7676
load_csv = LoadCsv
7777
MemgraphQueryBuilder = QueryBuilder
78+
79+
80+
def validator(*args, **kwargs):
81+
global _validator_deprecation_warning_shown
82+
if not _validator_deprecation_warning_shown:
83+
warnings.warn(
84+
"Importing 'validator' from this module is deprecated and will be "
85+
"removed in a future release. For Pydantic v2, migrate to "
86+
"'field_validator' where applicable, or keep importing "
87+
"'validator' from 'pydantic' for v1 compatibility.",
88+
DeprecationWarning,
89+
stacklevel=2,
90+
)
91+
_validator_deprecation_warning_shown = True
92+
return _validator(*args, **kwargs)

gqlalchemy/connection.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def _create_connection(self) -> Connection:
119119
def _convert_memgraph_value(value: Any) -> Any:
120120
"""Converts Memgraph objects to custom Node/Relationship objects."""
121121
if isinstance(value, mgclient.Relationship):
122-
return Relationship.parse_obj(
122+
return Relationship.model_validate(
123123
{
124124
"_type": value.type,
125125
"_id": value.id,
@@ -130,7 +130,7 @@ def _convert_memgraph_value(value: Any) -> Any:
130130
)
131131

132132
if isinstance(value, mgclient.Node):
133-
return Node.parse_obj(
133+
return Node.model_validate(
134134
{
135135
"_id": value.id,
136136
"_labels": set(value.labels),
@@ -139,7 +139,7 @@ def _convert_memgraph_value(value: Any) -> Any:
139139
)
140140

141141
if isinstance(value, mgclient.Path):
142-
return Path.parse_obj(
142+
return Path.model_validate(
143143
{
144144
"_nodes": list([_convert_memgraph_value(node) for node in value.nodes]),
145145
"_relationships": list([_convert_memgraph_value(rel) for rel in value.relationships]),
@@ -192,7 +192,7 @@ def _create_connection(self):
192192
def _convert_neo4j_value(value: Any) -> Any:
193193
"""Converts Neo4j objects to custom Node/Relationship objects."""
194194
if isinstance(value, Neo4jRelationship):
195-
return Relationship.parse_obj(
195+
return Relationship.model_validate(
196196
{
197197
"_type": value.type,
198198
"_id": value.id,
@@ -203,7 +203,7 @@ def _convert_neo4j_value(value: Any) -> Any:
203203
)
204204

205205
if isinstance(value, Neo4jNode):
206-
return Node.parse_obj(
206+
return Node.model_validate(
207207
{
208208
"_id": value.id,
209209
"_labels": set(value.labels),
@@ -212,7 +212,7 @@ def _convert_neo4j_value(value: Any) -> Any:
212212
)
213213

214214
if isinstance(value, Neo4jPath):
215-
return Path.parse_obj(
215+
return Path.model_validate(
216216
{
217217
"_nodes": list([_convert_neo4j_value(node) for node in value.nodes]),
218218
"_relationships": list([_convert_neo4j_value(rel) for rel in value.relationships]),

0 commit comments

Comments
 (0)