diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ecfe534a4..c56a6ceba 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 diff --git a/tests/test_applications.py b/tests/test_applications.py index 20ff06385..7bbc2b5b2 100644 --- a/tests/test_applications.py +++ b/tests/test_applications.py @@ -95,6 +95,20 @@ def custom_ws_exception_handler(websocket: WebSocket, exc: CustomWSException) -> anyio.from_thread.run(websocket.close, status.WS_1013_TRY_AGAIN_LATER) +class ExampleState( +): + count: int + + +@asynccontextmanager +async def lifespan(app: Starlette) -> AsyncGenerator[ExampleState]: + yield {"count": 1} + + +async def request_state(request: Request[ExampleState]) -> JSONResponse: + return JSONResponse({"state.count": request.state["count"]}, status_code=200) + + users = Router( routes=[ Route("/", endpoint=all_users_page), @@ -123,6 +137,7 @@ def custom_ws_exception_handler(websocket: WebSocket, exc: CustomWSException) -> Route("/async", endpoint=async_homepage), Route("/class", endpoint=Homepage), Route("/500", endpoint=runtime_error), + Route("/state", endpoint=request_state), WebSocketRoute("/ws", endpoint=websocket_endpoint), WebSocketRoute("/ws-raise-websocket", endpoint=websocket_raise_websocket_exception), WebSocketRoute("/ws-raise-http", endpoint=websocket_raise_http_exception), @@ -132,6 +147,7 @@ def custom_ws_exception_handler(websocket: WebSocket, exc: CustomWSException) -> ], exception_handlers=exception_handlers, # type: ignore middleware=middleware, + lifespan=lifespan, ) @@ -216,6 +232,12 @@ def test_500(test_client_factory: TestClientFactory) -> None: assert response.json() == {"detail": "Server Error"} +def test_request_state(client: TestClient) -> None: + response = client.get("/state") + assert response.status_code == 200 + assert response.json() == {"state.count": 1} + + def test_websocket_raise_websocket_exception(client: TestClient) -> None: with client.websocket_connect("/ws-raise-websocket") as session: response = session.receive() @@ -257,6 +279,7 @@ def test_routes() -> None: Route("/async", endpoint=async_homepage, methods=["GET"]), Route("/class", endpoint=Homepage), Route("/500", endpoint=runtime_error, methods=["GET"]), + Route("/state", endpoint=request_state, methods=["GET"]), WebSocketRoute("/ws", endpoint=websocket_endpoint), WebSocketRoute("/ws-raise-websocket", endpoint=websocket_raise_websocket_exception), WebSocketRoute("/ws-raise-http", endpoint=websocket_raise_http_exception), diff --git a/tests/test_requests.py b/tests/test_requests.py index 1799e89e0..f6f1a7086 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -297,11 +297,25 @@ def test_request_state_object() -> None: s.new = "value" assert s.new == "value" - del s.new + s["item"] = "baar" + assert s["item"] == "baar" + + assert len(s) == 3 + + assert "item" in s + + assert "old" in s + assert "new" in s + + del s.new with pytest.raises(AttributeError): s.new + del s["item"] + with pytest.raises(KeyError): + s["item"] + def test_request_state(test_client_factory: TestClientFactory) -> None: async def app(scope: Scope, receive: Receive, send: Send) -> None: