From dff1a46264e2ec213afbff66b91db9a52ad12459 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Sat, 4 Nov 2023 06:23:51 -0500 Subject: [PATCH] Support Python 3.12 (#2145) Co-authored-by: Marcelo Trylesinski --- .github/workflows/test-suite.yml | 2 +- pyproject.toml | 1 + tests/protocols/test_websocket.py | 24 ++++++++++++++++++++++++ uvicorn/server.py | 5 +++-- 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index c60b27b85..327c53db9 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -13,7 +13,7 @@ jobs: runs-on: "${{ matrix.os }}" strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] os: [windows-latest, ubuntu-latest, macos-latest] steps: - uses: "actions/checkout@v4" diff --git a/pyproject.toml b/pyproject.toml index 56bd79035..5d83c0f0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Internet :: WWW/HTTP", diff --git a/tests/protocols/test_websocket.py b/tests/protocols/test_websocket.py index 17f2a92d1..9f98e3c7b 100644 --- a/tests/protocols/test_websocket.py +++ b/tests/protocols/test_websocket.py @@ -109,6 +109,30 @@ async def open_connection(url): assert is_open +@pytest.mark.anyio +@pytest.mark.parametrize("http_protocol_cls", HTTP_PROTOCOLS) +async def test_shutdown( + ws_protocol_cls: "typing.Type[WSProtocol | WebSocketProtocol]", + http_protocol_cls, + unused_tcp_port: int, +): + class App(WebSocketResponse): + async def websocket_connect(self, message): + await self.send({"type": "websocket.accept"}) + + config = Config( + app=App, + ws=ws_protocol_cls, + http=http_protocol_cls, + lifespan="off", + port=unused_tcp_port, + ) + async with run_server(config) as server: + async with websockets.client.connect(f"ws://127.0.0.1:{unused_tcp_port}"): + # Attempt shutdown while connection is still open + await server.shutdown() + + @pytest.mark.anyio @pytest.mark.parametrize("http_protocol_cls", HTTP_PROTOCOLS) async def test_supports_permessage_deflate_extension( diff --git a/uvicorn/server.py b/uvicorn/server.py index 3e0db9d01..9e904b269 100644 --- a/uvicorn/server.py +++ b/uvicorn/server.py @@ -268,8 +268,6 @@ async def shutdown(self, sockets: Optional[List[socket.socket]] = None) -> None: server.close() for sock in sockets or []: sock.close() - for server in self.servers: - await server.wait_closed() # Request shutdown on all existing connections. for connection in list(self.server_state.connections): @@ -312,6 +310,9 @@ async def _wait_tasks_to_complete(self) -> None: while self.server_state.tasks and not self.force_exit: await asyncio.sleep(0.1) + for server in self.servers: + await server.wait_closed() + def install_signal_handlers(self) -> None: if threading.current_thread() is not threading.main_thread(): # Signals can only be listened to from the main thread.