Skip to content

Commit 7a84fd6

Browse files
committed
fixes for 3.14
1 parent fc81194 commit 7a84fd6

File tree

5 files changed

+27
-7
lines changed

5 files changed

+27
-7
lines changed

tests/test_auto_detection.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import asyncio
22
import contextlib
33
import importlib
4+
import sys
45

56
import pytest
67

@@ -15,6 +16,10 @@
1516
expected_loop = "uvloop" # pragma: py-win32
1617
except ImportError: # pragma: py-not-win32
1718
expected_loop = "asyncio"
19+
except AttributeError:
20+
if sys.version_info < (3, 14):
21+
raise
22+
expected_loop = "asyncio"
1823

1924
try:
2025
importlib.import_module("httptools")

tests/test_compat.py

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

33
import asyncio
4+
import sys
45
from asyncio import AbstractEventLoop
56

67
import pytest
@@ -23,7 +24,7 @@ def test_asyncio_run__custom_loop_factory() -> None:
2324

2425

2526
def test_asyncio_run__passing_a_non_awaitable_callback_should_throw_error() -> None:
26-
with pytest.raises(ValueError):
27+
with pytest.raises(TypeError if sys.version_info >= (3, 14) else ValueError):
2728
asyncio_run(
2829
lambda: None, # type: ignore
2930
loop_factory=CustomLoop,

uvicorn/_compat.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@
55
from collections.abc import Callable, Coroutine
66
from typing import Any, TypeVar
77

8+
__all__ = ["asyncio_run", "iscoroutinefunction"]
9+
10+
if sys.version_info >= (3, 14):
11+
from inspect import iscoroutinefunction
12+
else:
13+
from asyncio import iscoroutinefunction
14+
815
_T = TypeVar("_T")
916

1017
if sys.version_info >= (3, 12):

uvicorn/config.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import click
1818

19+
from uvicorn._compat import iscoroutinefunction
1920
from uvicorn._types import ASGIApplication
2021
from uvicorn.importer import ImportFromStringError, import_from_string
2122
from uvicorn.logging import TRACE_LOG_LEVEL
@@ -453,10 +454,10 @@ def load(self) -> None:
453454
if inspect.isclass(self.loaded_app):
454455
use_asgi_3 = hasattr(self.loaded_app, "__await__")
455456
elif inspect.isfunction(self.loaded_app):
456-
use_asgi_3 = asyncio.iscoroutinefunction(self.loaded_app)
457+
use_asgi_3 = iscoroutinefunction(self.loaded_app)
457458
else:
458459
call = getattr(self.loaded_app, "__call__", None)
459-
use_asgi_3 = asyncio.iscoroutinefunction(call)
460+
use_asgi_3 = iscoroutinefunction(call)
460461
self.interface = "asgi3" if use_asgi_3 else "asgi2"
461462

462463
if self.interface == "wsgi":

uvicorn/loops/auto.py

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

33
import asyncio
4+
import sys
45
from collections.abc import Callable
56

67

7-
def auto_loop_factory(use_subprocess: bool = False) -> Callable[[], asyncio.AbstractEventLoop]:
8+
def auto_loop_factory(use_subprocess: bool = False) -> Callable[[], asyncio.AbstractEventLoop]: # pragma: no cover
89
try:
910
import uvloop # noqa
1011
except ImportError: # pragma: no cover
11-
from uvicorn.loops.asyncio import asyncio_loop_factory as loop_factory
12-
13-
return loop_factory(use_subprocess=use_subprocess)
12+
pass
13+
except AttributeError: # pragma: no cover
14+
if sys.version_info < (3, 14):
15+
raise
1416
else: # pragma: no cover
1517
from uvicorn.loops.uvloop import uvloop_loop_factory
1618

1719
return uvloop_loop_factory(use_subprocess=use_subprocess)
20+
21+
from uvicorn.loops.asyncio import asyncio_loop_factory as loop_factory
22+
23+
return loop_factory(use_subprocess=use_subprocess)

0 commit comments

Comments
 (0)