Skip to content

Commit 04d56ca

Browse files
authored
Official support python3.14 (#2026)
* refactor: only imports AbstractEventLoop when type checking * refactor: use from thread instead of anyio.run * fix union annotation error * Add py3.14 to ci * chore: upgrade deps
1 parent 6a32975 commit 04d56ca

File tree

6 files changed

+2711
-2469
lines changed

6 files changed

+2711
-2469
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
TORTOISE_MSSQL_DRIVER: ODBC Driver 18 for SQL Server
4646
strategy:
4747
matrix:
48-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
48+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
4949
steps:
5050
- uses: actions/cache@v4
5151
with:

tests/contrib/test_decorator.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88
class TestDecorator(test.TestCase):
99
@test.requireCapability(dialect="sqlite")
1010
async def test_script_with_init_memory_sqlite(self) -> None:
11-
r = subprocess.run(["python", "examples/basic.py"], capture_output=True) # nosec
12-
output = r.stdout.decode()
11+
r = subprocess.run(["python", "examples/basic.py"], capture_output=True, text=True) # nosec
12+
assert not r.stderr
13+
output = r.stdout
1314
s = "[{'id': 1, 'name': 'Updated name'}, {'id': 2, 'name': 'Test 2'}]"
1415
self.assertIn(s, output)
1516

tests/utils/test_describe_model.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import json
4+
import sys
45
import uuid
56
from typing import Union
67

@@ -29,6 +30,18 @@
2930
OneToOneFieldInstance,
3031
)
3132

33+
if sys.version_info >= (3, 14):
34+
35+
def union_annotation(x: str, y: str) -> str:
36+
return f"{x} | {y}"
37+
else:
38+
39+
def union_annotation(x: str, y: str) -> str:
40+
return f"Union[{x}, {y}]"
41+
42+
43+
UNION_DICT_LIST = union_annotation("dict", "list")
44+
3245

3346
class TestDescribeModels(test.TestCase):
3447
def test_describe_models_all_serializable(self):
@@ -1366,7 +1379,7 @@ def test_describe_model_json(self):
13661379
"oracle": "NCLOB",
13671380
"postgres": "JSONB",
13681381
},
1369-
"python_type": "Union[dict, list]",
1382+
"python_type": UNION_DICT_LIST,
13701383
"generated": False,
13711384
"nullable": False,
13721385
"unique": False,
@@ -1386,7 +1399,7 @@ def test_describe_model_json(self):
13861399
"oracle": "NCLOB",
13871400
"postgres": "JSONB",
13881401
},
1389-
"python_type": "Union[dict, list]",
1402+
"python_type": UNION_DICT_LIST,
13901403
"generated": False,
13911404
"nullable": True,
13921405
"unique": False,
@@ -1406,7 +1419,7 @@ def test_describe_model_json(self):
14061419
"oracle": "NCLOB",
14071420
"postgres": "JSONB",
14081421
},
1409-
"python_type": "Union[dict, list]",
1422+
"python_type": UNION_DICT_LIST,
14101423
"generated": False,
14111424
"nullable": False,
14121425
"unique": False,
@@ -1426,7 +1439,7 @@ def test_describe_model_json(self):
14261439
"oracle": "NCLOB",
14271440
"postgres": "JSONB",
14281441
},
1429-
"python_type": "Union[dict, list]",
1442+
"python_type": UNION_DICT_LIST,
14301443
"generated": False,
14311444
"nullable": True,
14321445
"unique": False,

tortoise/__init__.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import asyncio
43
import importlib
54
import json
65
import logging
@@ -12,6 +11,7 @@
1211
from types import ModuleType
1312
from typing import Any, cast
1413

14+
from anyio import from_thread
1515
from pypika_tortoise import Query, Table
1616

1717
from tortoise.backends.base.client import BaseDBAsyncClient
@@ -637,11 +637,15 @@ async def do_stuff():
637637
638638
run_async(do_stuff())
639639
"""
640-
loop = asyncio.get_event_loop()
641-
try:
642-
loop.run_until_complete(coro)
643-
finally:
644-
loop.run_until_complete(connections.close_all(discard=True))
640+
641+
async def main() -> None:
642+
try:
643+
await coro
644+
finally:
645+
await connections.close_all(discard=True)
646+
647+
with from_thread.start_blocking_portal() as portal:
648+
portal.call(main)
645649

646650

647651
__version__ = "0.25.1"

tortoise/contrib/test/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
import sys
77
import typing
88
import unittest
9-
from asyncio.events import AbstractEventLoop
109
from collections.abc import Callable, Coroutine, Iterable
1110
from functools import partial, wraps
1211
from types import ModuleType
13-
from typing import Any, TypeVar, Union, cast
12+
from typing import TYPE_CHECKING, Any, TypeVar, Union, cast
1413
from unittest import SkipTest, expectedFailure, skip, skipIf, skipUnless
1514

1615
from tortoise import Model, Tortoise, connections
@@ -22,6 +21,9 @@
2221
else:
2322
from typing_extensions import ParamSpec
2423

24+
if TYPE_CHECKING:
25+
from asyncio.events import AbstractEventLoop
26+
2527

2628
__all__ = (
2729
"MEMORY_SQLITE",

0 commit comments

Comments
 (0)