From 0a1591e46476b938b5786fd390aff2d2254196dd Mon Sep 17 00:00:00 2001 From: Kim Christie Date: Fri, 12 Sep 2025 12:14:49 +0100 Subject: [PATCH 1/2] Add Python 3.14 to test matrix --- .github/workflows/test-suite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 92e8c36015..f60527dbb6 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: "actions/checkout@v4" From 609a5d4958fbbfab77dc1c540b0908eeda8758fa Mon Sep 17 00:00:00 2001 From: Mirthe Beijers Date: Fri, 17 Oct 2025 14:16:49 +0200 Subject: [PATCH 2/2] Fix running tests on python 3.14 See https://github.com/encode/httpx/pull/3664 See https://github.com/encode/httpx/discussions/3616 --- pyproject.toml | 4 +++- tests/conftest.py | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fc3e95ea74..2adc2625d7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -120,7 +120,9 @@ filterwarnings = [ "error", "ignore: You seem to already have a custom sys.excepthook handler installed. I'll skip installing Trio's custom handler, but this means MultiErrors will not show full tracebacks.:RuntimeWarning", # See: https://github.com/agronholm/anyio/issues/508 - "ignore: trio.MultiError is deprecated since Trio 0.22.0:trio.TrioDeprecationWarning" + "ignore: trio.MultiError is deprecated since Trio 0.22.0:trio.TrioDeprecationWarning", + # Python 3.14 deprecates asyncio.iscoroutinefunction, but uvicorn 0.35.0 still uses it + "ignore: 'asyncio.iscoroutinefunction' is deprecated:DeprecationWarning" ] markers = [ "copied_from(source, changes=None): mark test as copied from somewhere else, along with a description of changes made to accodomate e.g. our test setup", diff --git a/tests/conftest.py b/tests/conftest.py index 858bca1397..c04976d936 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -235,10 +235,9 @@ def install_signal_handlers(self) -> None: async def serve(self, sockets=None): self.restart_requested = asyncio.Event() - loop = asyncio.get_event_loop() tasks = { - loop.create_task(super().serve(sockets=sockets)), - loop.create_task(self.watch_restarts()), + asyncio.create_task(super().serve(sockets=sockets)), + asyncio.create_task(self.watch_restarts()), } await asyncio.wait(tasks) @@ -269,7 +268,15 @@ async def watch_restarts(self) -> None: # pragma: no cover def serve_in_thread(server: TestServer) -> typing.Iterator[TestServer]: - thread = threading.Thread(target=server.run) + def run_server(): + # Manually set up event loop for Python 3.14 compatibility + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + server.config.setup_event_loop() + loop.run_until_complete(server.serve()) + loop.close() + + thread = threading.Thread(target=run_server) thread.start() try: while not server.started: @@ -282,6 +289,6 @@ def serve_in_thread(server: TestServer) -> typing.Iterator[TestServer]: @pytest.fixture(scope="session") def server() -> typing.Iterator[TestServer]: - config = Config(app=app, lifespan="off", loop="asyncio") + config = Config(app=app, lifespan="off") server = TestServer(config=config) yield from serve_in_thread(server)