Coverage for tests / test_dependency_yield_scope.py: 100%
141 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
1import json 1abcd
2from typing import Annotated, Any 1abcd
4import pytest 1abcd
5from fastapi import APIRouter, Depends, FastAPI, HTTPException 1abcd
6from fastapi.exceptions import FastAPIError 1abcd
7from fastapi.responses import StreamingResponse 1abcd
8from fastapi.testclient import TestClient 1abcd
11class Session: 1abcd
12 def __init__(self) -> None: 1abcd
13 self.open = True 1shkteouvwilxfpyjmzgq
16def dep_session() -> Any: 1abcd
17 s = Session() 1shkteouvwilxfpyjmzgq
18 yield s 1shkteouvwilxfpyjmzgq
19 s.open = False 1shkteouvwilxfpyjmzgq
22def raise_after_yield() -> Any: 1abcd
23 yield 1BCOKPLDEQMFGRN
24 raise HTTPException(status_code=503, detail="Exception after yield") 1BCOKPLDEQMFGRN
27SessionFuncDep = Annotated[Session, Depends(dep_session, scope="function")] 1abcd
28SessionRequestDep = Annotated[Session, Depends(dep_session, scope="request")] 1abcd
29SessionDefaultDep = Annotated[Session, Depends(dep_session)] 1abcd
32class NamedSession: 1abcd
33 def __init__(self, name: str = "default") -> None: 1abcd
34 self.name = name 1hkernilfjmg
35 self.open = True 1hkernilfjmg
38def get_named_session(session: SessionRequestDep, session_b: SessionDefaultDep) -> Any: 1abcd
39 assert session is session_b 1enfg
40 named_session = NamedSession(name="named") 1enfg
41 yield named_session, session_b 1enfg
42 named_session.open = False 1enfg
45NamedSessionsDep = Annotated[tuple[NamedSession, Session], Depends(get_named_session)] 1abcd
48def get_named_func_session(session: SessionFuncDep) -> Any: 1abcd
49 named_session = NamedSession(name="named") 1hAij
50 yield named_session, session 1hAij
51 named_session.open = False 1hAij
54def get_named_regular_func_session(session: SessionFuncDep) -> Any: 1abcd
55 named_session = NamedSession(name="named") 1krlm
56 return named_session, session 1krlm
59BrokenSessionsDep = Annotated[ 1abcd
60 tuple[NamedSession, Session], Depends(get_named_func_session)
61]
62NamedSessionsFuncDep = Annotated[ 1abcd
63 tuple[NamedSession, Session], Depends(get_named_func_session, scope="function")
64]
66RegularSessionsDep = Annotated[ 1abcd
67 tuple[NamedSession, Session], Depends(get_named_regular_func_session)
68]
70app = FastAPI() 1abcd
71router = APIRouter() 1abcd
74@router.get("/") 1abcd
75def get_index(): 1abcd
76 return {"status": "ok"} 1OKPLQMRN
79@app.get("/function-scope") 1abcd
80def function_scope(session: SessionFuncDep) -> Any: 1abcd
81 def iter_data(): 1suwy
82 yield json.dumps({"is_open": session.open}) 1suwy
84 return StreamingResponse(iter_data()) 1suwy
87@app.get("/request-scope") 1abcd
88def request_scope(session: SessionRequestDep) -> Any: 1abcd
89 def iter_data(): 1tvxz
90 yield json.dumps({"is_open": session.open}) 1tvxz
92 return StreamingResponse(iter_data()) 1tvxz
95@app.get("/two-scopes") 1abcd
96def get_stream_session( 1abcd
97 function_session: SessionFuncDep, request_session: SessionRequestDep
98) -> Any:
99 def iter_data(): 1oHpq
100 yield json.dumps( 1oHpq
101 {"func_is_open": function_session.open, "req_is_open": request_session.open}
102 )
104 return StreamingResponse(iter_data()) 1oHpq
107@app.get("/sub") 1abcd
108def get_sub(sessions: NamedSessionsDep) -> Any: 1abcd
109 def iter_data(): 1enfg
110 yield json.dumps( 1enfg
111 {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
112 )
114 return StreamingResponse(iter_data()) 1enfg
117@app.get("/named-function-scope") 1abcd
118def get_named_function_scope(sessions: NamedSessionsFuncDep) -> Any: 1abcd
119 def iter_data(): 1hAij
120 yield json.dumps( 1hAij
121 {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
122 )
124 return StreamingResponse(iter_data()) 1hAij
127@app.get("/regular-function-scope") 1abcd
128def get_regular_function_scope(sessions: RegularSessionsDep) -> Any: 1abcd
129 def iter_data(): 1krlm
130 yield json.dumps( 1krlm
131 {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
132 )
134 return StreamingResponse(iter_data()) 1krlm
137app.include_router( 1abcd
138 prefix="/router-scope-function",
139 router=router,
140 dependencies=[Depends(raise_after_yield, scope="function")],
141)
143app.include_router( 1abcd
144 prefix="/router-scope-request",
145 router=router,
146 dependencies=[Depends(raise_after_yield, scope="request")],
147)
149client = TestClient(app) 1abcd
152def test_function_scope() -> None: 1abcd
153 response = client.get("/function-scope") 1suwy
154 assert response.status_code == 200 1suwy
155 data = response.json() 1suwy
156 assert data["is_open"] is False 1suwy
159def test_request_scope() -> None: 1abcd
160 response = client.get("/request-scope") 1tvxz
161 assert response.status_code == 200 1tvxz
162 data = response.json() 1tvxz
163 assert data["is_open"] is True 1tvxz
166def test_two_scopes() -> None: 1abcd
167 response = client.get("/two-scopes") 1oHpq
168 assert response.status_code == 200 1oHpq
169 data = response.json() 1oHpq
170 assert data["func_is_open"] is False 1oHpq
171 assert data["req_is_open"] is True 1oHpq
174def test_sub() -> None: 1abcd
175 response = client.get("/sub") 1enfg
176 assert response.status_code == 200 1enfg
177 data = response.json() 1enfg
178 assert data["named_session_open"] is True 1enfg
179 assert data["session_open"] is True 1enfg
182def test_broken_scope() -> None: 1abcd
183 with pytest.raises( 1STUV
184 FastAPIError,
185 match='The dependency "get_named_func_session" has a scope of "request", it cannot depend on dependencies with scope "function"',
186 ):
188 @app.get("/broken-scope") 1STUV
189 def get_broken(sessions: BrokenSessionsDep) -> Any: # pragma: no cover 1STUV
190 pass
193def test_named_function_scope() -> None: 1abcd
194 response = client.get("/named-function-scope") 1hAij
195 assert response.status_code == 200 1hAij
196 data = response.json() 1hAij
197 assert data["named_session_open"] is False 1hAij
198 assert data["session_open"] is False 1hAij
201def test_regular_function_scope() -> None: 1abcd
202 response = client.get("/regular-function-scope") 1krlm
203 assert response.status_code == 200 1krlm
204 data = response.json() 1krlm
205 assert data["named_session_open"] is True 1krlm
206 assert data["session_open"] is False 1krlm
209def test_router_level_dep_scope_function() -> None: 1abcd
210 response = client.get("/router-scope-function/") 1OPQR
211 assert response.status_code == 503 1OPQR
212 assert response.json() == {"detail": "Exception after yield"} 1OPQR
215def test_router_level_dep_scope_request() -> None: 1abcd
216 with TestClient(app, raise_server_exceptions=False) as client: 1KLMN
217 response = client.get("/router-scope-request/") 1KLMN
218 assert response.status_code == 200 1KLMN
219 assert response.json() == {"status": "ok"} 1KLMN
222def test_app_level_dep_scope_function() -> None: 1abcd
223 app = FastAPI(dependencies=[Depends(raise_after_yield, scope="function")]) 1BIDF
225 @app.get("/app-scope-function") 1BIDF
226 def get_app_scope_function(): 1BIDF
227 return {"status": "ok"} 1BIDF
229 with TestClient(app) as client: 1BIDF
230 response = client.get("/app-scope-function") 1BIDF
231 assert response.status_code == 503 1BIDF
232 assert response.json() == {"detail": "Exception after yield"} 1BIDF
235def test_app_level_dep_scope_request() -> None: 1abcd
236 app = FastAPI(dependencies=[Depends(raise_after_yield, scope="request")]) 1CJEG
238 @app.get("/app-scope-request") 1CJEG
239 def get_app_scope_request(): 1CJEG
240 return {"status": "ok"} 1CJEG
242 with TestClient(app, raise_server_exceptions=False) as client: 1CJEG
243 response = client.get("/app-scope-request") 1CJEG
244 assert response.status_code == 200 1CJEG
245 assert response.json() == {"status": "ok"} 1CJEG