Coverage for tests/test_dependency_yield_scope.py: 100%
142 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2025-12-04 08:29 +0000
1import json 1abcdefg
2from typing import Any, Tuple 1abcdefg
4import pytest 1abcdefg
5from fastapi import APIRouter, Depends, FastAPI, HTTPException 1abcdefg
6from fastapi.exceptions import FastAPIError 1abcdefg
7from fastapi.responses import StreamingResponse 1abcdefg
8from fastapi.testclient import TestClient 1abcdefg
9from typing_extensions import Annotated 1abcdefg
12class Session: 1abcdefg
13 def __init__(self) -> None: 1abcdefg
14 self.open = True 1JovKhCLpwMiDNqxOjEPryQkFRszSlGTtAUmHVuBWnI
17def dep_session() -> Any: 1abcdefg
18 s = Session() 1JovKhCLpwMiDNqxOjEPryQkFRszSlGTtAUmHVuBWnI
19 yield s 1JovKhCLpwMiDNqxOjEPryQkFRszSlGTtAUmHVuBWnI
20 s.open = False 1JovKhCLpwMiDNqxOjEPryQkFRszSlGTtAUmHVuBWnI
23def raise_after_yield() -> Any: 1abcdefg
24 yield 1XY+#Z0,$12-%34.'56/(78:)9!;*
25 raise HTTPException(status_code=503, detail="Exception after yield") 1XY+#Z0,$12-%34.'56/(78:)9!;*
28SessionFuncDep = Annotated[Session, Depends(dep_session, scope="function")] 1abcdefg
29SessionRequestDep = Annotated[Session, Depends(dep_session, scope="request")] 1abcdefg
30SessionDefaultDep = Annotated[Session, Depends(dep_session)] 1abcdefg
33class NamedSession: 1abcdefg
34 def __init__(self, name: str = "default") -> None: 1abcdefg
35 self.name = name 1ovhpwiqxjrykszltAmuBn
36 self.open = True 1ovhpwiqxjrykszltAmuBn
39def get_named_session(session: SessionRequestDep, session_b: SessionDefaultDep) -> Any: 1abcdefg
40 assert session is session_b 1hijklmn
41 named_session = NamedSession(name="named") 1hijklmn
42 yield named_session, session_b 1hijklmn
43 named_session.open = False 1hijklmn
46NamedSessionsDep = Annotated[Tuple[NamedSession, Session], Depends(get_named_session)] 1abcdefg
49def get_named_func_session(session: SessionFuncDep) -> Any: 1abcdefg
50 named_session = NamedSession(name="named") 1opqrstu
51 yield named_session, session 1opqrstu
52 named_session.open = False 1opqrstu
55def get_named_regular_func_session(session: SessionFuncDep) -> Any: 1abcdefg
56 named_session = NamedSession(name="named") 1vwxyzAB
57 return named_session, session 1vwxyzAB
60BrokenSessionsDep = Annotated[ 1abcdefg
61 Tuple[NamedSession, Session], Depends(get_named_func_session)
62]
63NamedSessionsFuncDep = Annotated[ 1abcdefg
64 Tuple[NamedSession, Session], Depends(get_named_func_session, scope="function")
65]
67RegularSessionsDep = Annotated[ 1abcdefg
68 Tuple[NamedSession, Session], Depends(get_named_regular_func_session)
69]
71app = FastAPI() 1abcdefg
72router = APIRouter() 1abcdefg
75@router.get("/") 1abcdefg
76def get_index(): 1abcdefg
77 return {"status": "ok"} 1+#,$-%.'/(:);*
80@app.get("/function-scope") 1abcdefg
81def function_scope(session: SessionFuncDep) -> Any: 1abcdefg
82 def iter_data(): 1JLNPRTV
83 yield json.dumps({"is_open": session.open}) 1JLNPRTV
85 return StreamingResponse(iter_data()) 1JLNPRTV
88@app.get("/request-scope") 1abcdefg
89def request_scope(session: SessionRequestDep) -> Any: 1abcdefg
90 def iter_data(): 1KMOQSUW
91 yield json.dumps({"is_open": session.open}) 1KMOQSUW
93 return StreamingResponse(iter_data()) 1KMOQSUW
96@app.get("/two-scopes") 1abcdefg
97def get_stream_session( 1abcdefg
98 function_session: SessionFuncDep, request_session: SessionRequestDep
99) -> Any:
100 def iter_data(): 1CDEFGHI
101 yield json.dumps( 1CDEFGHI
102 {"func_is_open": function_session.open, "req_is_open": request_session.open}
103 )
105 return StreamingResponse(iter_data()) 1CDEFGHI
108@app.get("/sub") 1abcdefg
109def get_sub(sessions: NamedSessionsDep) -> Any: 1abcdefg
110 def iter_data(): 1hijklmn
111 yield json.dumps( 1hijklmn
112 {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
113 )
115 return StreamingResponse(iter_data()) 1hijklmn
118@app.get("/named-function-scope") 1abcdefg
119def get_named_function_scope(sessions: NamedSessionsFuncDep) -> Any: 1abcdefg
120 def iter_data(): 1opqrstu
121 yield json.dumps( 1opqrstu
122 {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
123 )
125 return StreamingResponse(iter_data()) 1opqrstu
128@app.get("/regular-function-scope") 1abcdefg
129def get_regular_function_scope(sessions: RegularSessionsDep) -> Any: 1abcdefg
130 def iter_data(): 1vwxyzAB
131 yield json.dumps( 1vwxyzAB
132 {"named_session_open": sessions[0].open, "session_open": sessions[1].open}
133 )
135 return StreamingResponse(iter_data()) 1vwxyzAB
138app.include_router( 1abcdefg
139 prefix="/router-scope-function",
140 router=router,
141 dependencies=[Depends(raise_after_yield, scope="function")],
142)
144app.include_router( 1abcdefg
145 prefix="/router-scope-request",
146 router=router,
147 dependencies=[Depends(raise_after_yield, scope="request")],
148)
150client = TestClient(app) 1abcdefg
153def test_function_scope() -> None: 1abcdefg
154 response = client.get("/function-scope") 1JLNPRTV
155 assert response.status_code == 200 1JLNPRTV
156 data = response.json() 1JLNPRTV
157 assert data["is_open"] is False 1JLNPRTV
160def test_request_scope() -> None: 1abcdefg
161 response = client.get("/request-scope") 1KMOQSUW
162 assert response.status_code == 200 1KMOQSUW
163 data = response.json() 1KMOQSUW
164 assert data["is_open"] is True 1KMOQSUW
167def test_two_scopes() -> None: 1abcdefg
168 response = client.get("/two-scopes") 1CDEFGHI
169 assert response.status_code == 200 1CDEFGHI
170 data = response.json() 1CDEFGHI
171 assert data["func_is_open"] is False 1CDEFGHI
172 assert data["req_is_open"] is True 1CDEFGHI
175def test_sub() -> None: 1abcdefg
176 response = client.get("/sub") 1hijklmn
177 assert response.status_code == 200 1hijklmn
178 data = response.json() 1hijklmn
179 assert data["named_session_open"] is True 1hijklmn
180 assert data["session_open"] is True 1hijklmn
183def test_broken_scope() -> None: 1abcdefg
184 with pytest.raises( 1=?@[]^_
185 FastAPIError,
186 match='The dependency "get_named_func_session" has a scope of "request", it cannot depend on dependencies with scope "function"',
187 ):
189 @app.get("/broken-scope") 1=?@[]^_
190 def get_broken(sessions: BrokenSessionsDep) -> Any: # pragma: no cover 1=?@[]^_
191 pass
194def test_named_function_scope() -> None: 1abcdefg
195 response = client.get("/named-function-scope") 1opqrstu
196 assert response.status_code == 200 1opqrstu
197 data = response.json() 1opqrstu
198 assert data["named_session_open"] is False 1opqrstu
199 assert data["session_open"] is False 1opqrstu
202def test_regular_function_scope() -> None: 1abcdefg
203 response = client.get("/regular-function-scope") 1vwxyzAB
204 assert response.status_code == 200 1vwxyzAB
205 data = response.json() 1vwxyzAB
206 assert data["named_session_open"] is True 1vwxyzAB
207 assert data["session_open"] is False 1vwxyzAB
210def test_router_level_dep_scope_function() -> None: 1abcdefg
211 response = client.get("/router-scope-function/") 1+,-./:;
212 assert response.status_code == 503 1+,-./:;
213 assert response.json() == {"detail": "Exception after yield"} 1+,-./:;
216def test_router_level_dep_scope_request() -> None: 1abcdefg
217 with TestClient(app, raise_server_exceptions=False) as client: 1#$%'()*
218 response = client.get("/router-scope-request/") 1#$%'()*
219 assert response.status_code == 200 1#$%'()*
220 assert response.json() == {"status": "ok"} 1#$%'()*
223def test_app_level_dep_scope_function() -> None: 1abcdefg
224 app = FastAPI(dependencies=[Depends(raise_after_yield, scope="function")]) 1XZ13579
226 @app.get("/app-scope-function") 1XZ13579
227 def get_app_scope_function(): 1XZ13579
228 return {"status": "ok"} 1XZ13579
230 with TestClient(app) as client: 1XZ13579
231 response = client.get("/app-scope-function") 1XZ13579
232 assert response.status_code == 503 1XZ13579
233 assert response.json() == {"detail": "Exception after yield"} 1XZ13579
236def test_app_level_dep_scope_request() -> None: 1abcdefg
237 app = FastAPI(dependencies=[Depends(raise_after_yield, scope="request")]) 1Y02468!
239 @app.get("/app-scope-request") 1Y02468!
240 def get_app_scope_request(): 1Y02468!
241 return {"status": "ok"} 1Y02468!
243 with TestClient(app, raise_server_exceptions=False) as client: 1Y02468!
244 response = client.get("/app-scope-request") 1Y02468!
245 assert response.status_code == 200 1Y02468!
246 assert response.json() == {"status": "ok"} 1Y02468!