Coverage for tests / test_dependency_yield_scope.py: 100%
141 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-12 18:15 +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 1qhkrensiltfoujmvgp
16def dep_session() -> Any: 1abcd
17 s = Session() 1qhkrensiltfoujmvgp
18 yield s 1qhkrensiltfoujmvgp
19 s.open = False 1qhkrensiltfoujmvgp
22def raise_after_yield() -> Any: 1abcd
23 yield 1wxFCyzGDABHE
24 raise HTTPException(status_code=503, detail="Exception after yield") 1wxFCyzGDABHE
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 1hkeilfjmg
35 self.open = True 1hkeilfjmg
38def get_named_session(session: SessionRequestDep, session_b: SessionDefaultDep) -> Any: 1abcd
39 assert session is session_b 1efg
40 named_session = NamedSession(name="named") 1efg
41 yield named_session, session_b 1efg
42 named_session.open = False 1efg
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") 1hij
50 yield named_session, session 1hij
51 named_session.open = False 1hij
54def get_named_regular_func_session(session: SessionFuncDep) -> Any: 1abcd
55 named_session = NamedSession(name="named") 1klm
56 return named_session, session 1klm
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"} 1FCGDHE
79@app.get("/function-scope") 1abcd
80def function_scope(session: SessionFuncDep) -> Any: 1abcd
81 def iter_data(): 1qsu
82 yield json.dumps({"is_open": session.open}) 1qsu
84 return StreamingResponse(iter_data()) 1qsu
87@app.get("/request-scope") 1abcd
88def request_scope(session: SessionRequestDep) -> Any: 1abcd
89 def iter_data(): 1rtv
90 yield json.dumps({"is_open": session.open}) 1rtv
92 return StreamingResponse(iter_data()) 1rtv
95@app.get("/two-scopes") 1abcd
96def get_stream_session( 1abcd
97 function_session: SessionFuncDep, request_session: SessionRequestDep
98) -> Any:
99 def iter_data(): 1nop
100 yield json.dumps( 1nop
101 {"func_is_open": function_session.open, "req_is_open": request_session.open}
102 )
104 return StreamingResponse(iter_data()) 1nop
107@app.get("/sub") 1abcd
108def get_sub(sessions: NamedSessionsDep) -> Any: 1abcd
109 def iter_data(): 1efg
110 yield json.dumps( 1efg
111 {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
112 )
114 return StreamingResponse(iter_data()) 1efg
117@app.get("/named-function-scope") 1abcd
118def get_named_function_scope(sessions: NamedSessionsFuncDep) -> Any: 1abcd
119 def iter_data(): 1hij
120 yield json.dumps( 1hij
121 {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
122 )
124 return StreamingResponse(iter_data()) 1hij
127@app.get("/regular-function-scope") 1abcd
128def get_regular_function_scope(sessions: RegularSessionsDep) -> Any: 1abcd
129 def iter_data(): 1klm
130 yield json.dumps( 1klm
131 {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
132 )
134 return StreamingResponse(iter_data()) 1klm
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") 1qsu
154 assert response.status_code == 200 1qsu
155 data = response.json() 1qsu
156 assert data["is_open"] is False 1qsu
159def test_request_scope() -> None: 1abcd
160 response = client.get("/request-scope") 1rtv
161 assert response.status_code == 200 1rtv
162 data = response.json() 1rtv
163 assert data["is_open"] is True 1rtv
166def test_two_scopes() -> None: 1abcd
167 response = client.get("/two-scopes") 1nop
168 assert response.status_code == 200 1nop
169 data = response.json() 1nop
170 assert data["func_is_open"] is False 1nop
171 assert data["req_is_open"] is True 1nop
174def test_sub() -> None: 1abcd
175 response = client.get("/sub") 1efg
176 assert response.status_code == 200 1efg
177 data = response.json() 1efg
178 assert data["named_session_open"] is True 1efg
179 assert data["session_open"] is True 1efg
182def test_broken_scope() -> None: 1abcd
183 with pytest.raises( 1IJK
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") 1IJK
189 def get_broken(sessions: BrokenSessionsDep) -> Any: # pragma: no cover 1IJK
190 pass
193def test_named_function_scope() -> None: 1abcd
194 response = client.get("/named-function-scope") 1hij
195 assert response.status_code == 200 1hij
196 data = response.json() 1hij
197 assert data["named_session_open"] is False 1hij
198 assert data["session_open"] is False 1hij
201def test_regular_function_scope() -> None: 1abcd
202 response = client.get("/regular-function-scope") 1klm
203 assert response.status_code == 200 1klm
204 data = response.json() 1klm
205 assert data["named_session_open"] is True 1klm
206 assert data["session_open"] is False 1klm
209def test_router_level_dep_scope_function() -> None: 1abcd
210 response = client.get("/router-scope-function/") 1FGH
211 assert response.status_code == 503 1FGH
212 assert response.json() == {"detail": "Exception after yield"} 1FGH
215def test_router_level_dep_scope_request() -> None: 1abcd
216 with TestClient(app, raise_server_exceptions=False) as client: 1CDE
217 response = client.get("/router-scope-request/") 1CDE
218 assert response.status_code == 200 1CDE
219 assert response.json() == {"status": "ok"} 1CDE
222def test_app_level_dep_scope_function() -> None: 1abcd
223 app = FastAPI(dependencies=[Depends(raise_after_yield, scope="function")]) 1wyA
225 @app.get("/app-scope-function") 1wyA
226 def get_app_scope_function(): 1wyA
227 return {"status": "ok"} 1wyA
229 with TestClient(app) as client: 1wyA
230 response = client.get("/app-scope-function") 1wyA
231 assert response.status_code == 503 1wyA
232 assert response.json() == {"detail": "Exception after yield"} 1wyA
235def test_app_level_dep_scope_request() -> None: 1abcd
236 app = FastAPI(dependencies=[Depends(raise_after_yield, scope="request")]) 1xzB
238 @app.get("/app-scope-request") 1xzB
239 def get_app_scope_request(): 1xzB
240 return {"status": "ok"} 1xzB
242 with TestClient(app, raise_server_exceptions=False) as client: 1xzB
243 response = client.get("/app-scope-request") 1xzB
244 assert response.status_code == 200 1xzB
245 assert response.json() == {"status": "ok"} 1xzB